高级数据结构之赫夫曼树

article/2025/8/30 4:52:16

思考两个问题

  1. 电报发送:二战的时候大家都知道那时候普遍会应用电报,如果让你来设计一个电报的发送编码你该如何设计呢?
  1. 电报加密后越短越好,发送快。
  2. 破解难
  3. 解码容易
  4. 换加密树也要快
  5. 可逆的
  1. 压缩算法:给你10000个字符(每个字符1btye,也就是8bit)的文件,你怎么存储可以尽可能的节省空间呢?假设字符是 a b c d 4种,10000 * 8 =80000bit

    思路1:重复的去掉;思路2:使用二进制代替

假定 a=000 b=001 c=010 d=100,这样我们每个字符由于原来8bit就变成了3bit的二进制,缩小了将近3倍
举例分析:dab二进制为100000001,若是abcdaaa二进制为000001010100000000000,

出现很多重复的a是否可以进一步优化?
假设优化:A:0 B:001 C:010 D:100,则abcdaaa:0001010100000=>abcdaaa

进一步将重复的数据缩小了,是否存在问题?
abcdaaa解码,以前3个bit表示一个字符,现在b、c编码包含a,不支持解码,怎么优化?

优化思路:区分开a和b,赫夫曼编码(前缀编码)
优化:A:0 B:101 C:110 D:100,则abcdaaa编码0101110100000

解码伪代码如下

// A:0		B:101	C:110	D:100
Map mapping;
String code="01011101000000000"
var decode;
var currDecode;
for(int i=0;i<code.lenth;i++){currDecode+=char(i);var res = mapping.get(currDecode);if(res != null){decode+=res}
}

最优二叉树(哈夫曼树)

| 满二叉树 | 除了叶子节点,其他的都有两个子节点,1 2 4 8这样的节点 2^n个点
|
| — | — |
| 完全二叉树 | 除了最底层都有两个子节点,而且叶子节点是靠左连续的
|

image.png
计算这三颗二叉树的带权路径长度总和:其中每个点的权重为:a:7 b:5 c:2 d:4
公式:每个节点深度*权重的和

WPL(a):72+52+22+42=36()
WPL(b):73+53+21+42=46()
WPL©:71+52+23+43=35()
发现什么? 权重越大节点,深度越低,和越小,和最小的就是最优二叉树

定义:

给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近

那么这个赫夫曼树和压缩又有什么关系呢?

二叉树,二叉,这时候你要想到二进制,二叉分左右嘛。左节点的边设置为0,右节点的边设置为1

哈夫曼编码定义:

最优二叉树中我们给每一条边加上一个权值,指向左子节点的边我们标记为0,指向右子节点的边标记为1,那从根节点到叶节点的路径就是我们说的哈夫曼编码
image.png
所以图c的赫夫曼树对应的编码就是:
a:0 b:10 c:110 d:111

构建思路

核心思想:贪心算法:利用局部最优推出全局最优,把频率出现多的用短码表示,频率出现小的就用长一点。而且,任何一个字符的编码都不是另一个的前缀,在解压缩的时候,我们每次会读取尽可能长的可解压的二进制串,所以在解压缩的时候也不会产生歧义

具体实现思路:

  1. 按权重排序
  2. 每次取数值最小的两个节点,将之组成为一颗子树。
  3. 移除原来的两个点
  4. 然后将组成的子树放入原来的序列中
  5. 重复执行1 2 3 直到只剩最后一个点

实现哈夫曼树

例子: a:3 b:24 c:6 d:20 e:34 f:4 g:12,根据以上权重来实现哈夫曼树

构建哈夫曼树

package datastructure.tree;import java.util.*;/*** 哈夫曼树** @author zw* @create 2023-04-09 1:24*/
public class HuffmenTree<T extends Comparable<T>> {MyTreeNode<T> root;List<MyTreeNode<T>> leafs;                // 叶子节点Map<T, Integer> weights; // 叶子节点的权重, a,b,c,d,eMap<String, String> encodeMap = new HashMap<>(); // 编码表Map<String, String> decodeMap = new HashMap<>(); // 解码表public HuffmenTree(Map<T, Integer> weights) {this.weights = weights;leafs = new ArrayList<>();}/*** 构建哈夫曼树*/public void builder() {// 按权重排序,这可以用之前写得排序算法,为了方便使用jdk的优先队列PriorityQueue<MyTreeNode> priorityQueue = new PriorityQueue<MyTreeNode>((o1, o2) -> o1.weight - o2.weight);weights.forEach((data, weight) -> {MyTreeNode MyTreeNode = new MyTreeNode(data, weight);priorityQueue.add(MyTreeNode);leafs.add(MyTreeNode);});MyTreeNode root = null;while (priorityQueue.size() != 1) {// 从优先队列拿出最小的两个节点合并MyTreeNode leftNode = priorityQueue.poll();MyTreeNode rightNode = priorityQueue.poll();// 将合并节点放入优先对垒int parentNodeWight = leftNode.weight + rightNode.weight;MyTreeNode<T> parentNode = new MyTreeNode<T>(null, parentNodeWight);parentNode.left = leftNode;parentNode.right = rightNode;leftNode.parent = parentNode;rightNode.parent = parentNode;priorityQueue.add(parentNode);root = parentNode;}// 将根节点返回this.root = root;}/*** 解码* @param code* @return*/public String decode(String code) {String decode = "";char[] chars = code.toCharArray();String currCode = "";for (int i = 0; i < chars.length; i++) {currCode += chars[i];if (decodeMap.containsKey(currCode)) {String data = decodeMap.get(currCode);decode += data;currCode = "";}}return decode;}/*** 对输入编码** @param input*/public String encode(String input) {String code = "";for (char data : input.toCharArray()) {String encode = encodeMap.get(String.valueOf(data));code += encode;}return code;}/*** 对叶子节点编码** @return*/public void leafencode() {// 两种方式:1、自顶向下  2、自底向上,这儿采用自底向上for (MyTreeNode<T> leaf : leafs) {String code = "";T data = leaf.data;MyTreeNode currNode = leaf;while (currNode.parent != null) {// 判断当前节点,是左是右MyTreeNode parentNode = currNode.parent;if (parentNode.left == currNode) {code = "0" + code; // 自底向上编码是反的,这儿处理下} else {code = "1" + code;}currNode = currNode.parent;}encodeMap.put(data.toString(), code); // 初始化编码表decodeMap.put(code, data.toString());  // 初始化解码表}}
}

测试用例

    public static void main(String[] args) {// a:3 b:24 c:6 d:20 e:34 f:4 g:12Map<Character, Integer> weights = new HashMap<Character, Integer>();weights.put('a', 3);weights.put('b', 24);weights.put('c', 6);weights.put('d', 20);weights.put('e', 34);weights.put('f', 4);weights.put('g', 12);HuffmenTree huffmenTree = new HuffmenTree(weights);huffmenTree.builder();huffmenTree.leafencode();huffmenTree.root.show();String input = "aceg";String encode = huffmenTree.encode(input);String decode = huffmenTree.decode(encode);String log = String.format("编码表:%s\n解码表:%s\n输入:%s\n编码结果:%s\n解码结果:%s",huffmenTree.encodeMap,huffmenTree.decodeMap,input,encode,decode);System.out.println(log);}

运行结果

                                                null                                             /            \                                           null                null                                   /       \           /       \                                  d            b      null         e                              /         \                                           g           null                                     /  \                                      c       null                                 / \                                   a   f                                  
编码表:{a=10110, b=01, c=1010, d=00, e=11, f=10111, g=100}
解码表:{00=d, 11=e, 01=b, 100=g, 10110=a, 1010=c, 10111=f}
输入:aceg
编码结果:10110101011100
解码结果:acega   f                                  
编码表:{a=10110, b=01, c=1010, d=00, e=11, f=10111, g=100}
解码表:{00=d, 11=e, 01=b, 100=g, 10110=a, 1010=c, 10111=f}
输入:aceg
编码结果:10110101011100
解码结果:aceg

http://chatgpt.dhexx.cn/article/hXA3Lf9u.shtml

相关文章

ARM到底是冯诺依曼结构还是哈佛结构?

问题 嵌入式的学习中ARM处理器是主题&#xff0c;这些年产业界除了PC和服务器市场外&#xff0c;以手机、pad、家电控制等为代表的嵌入式领域都被ARM几乎垄断了。所以学习嵌入式处理器&#xff0c;其实等同于学习ARM。&#xff08;当然了&#xff0c;近两年RISC-V架构横空出世在…

冯诺依曼结构和哈佛结构的区别

冯诺依曼结构和哈佛结构的区别 1. 冯诺依曼结构&#xff1a; 说明&#xff1a; 一种将程序指令存储器和数据存储器合并在一起的存储器结构。程序指令存储地址和数据存储地址指向同一个存储器的不同物理位置&#xff0c;因此程序指令和数据的宽度相同。 冯诺依曼的计算机必须…

冯诺依曼与哈佛结构的区别

cortex M3,M4主要采用哈弗结构 个人理解&#xff1a;最主要的区别在于程序空间和数据空间是否是一体的&#xff0c;冯诺依曼结构数据空间和地址空间是不分开的&#xff0c;而哈佛结构数据空间和地址空间是分开的 哈弗结构的优势&#xff1a;如果采用流水线设计&#xff0…

冯氏结构、哈佛结构、超级哈佛结构之间的异同

冯.诺伊曼结构 1945年&#xff0c;冯.诺伊曼首先提出了“存储程序”的概念和二进制原理&#xff0c;后来&#xff0c;人们把利用这种概念和原理设计的电子计算机系统统称为“冯.诺伊曼型结构”计算机。冯.诺伊曼结构的处理器使用同一个存储器&#xff0c;经由同一个总线传输…

哈佛结构

数字信号处理一般需要较大的运算量和较高的运算速度&#xff0c;为了提高数据吞吐量&#xff0c;在数字信号处理器中大多采用哈佛结构&#xff0c;如下图所示 图 哈佛结构 与冯.诺曼结构处理器比较&#xff0c;哈佛结构处理器有两个明显的特点&#xff1a; 使用两个独立的存储…

冯诺依曼体系结构、哈佛体系结构与改进型哈佛结构之间的区别

1、冯诺依曼结构  冯诺依曼结构又称作普林斯顿体系结构&#xff08;Princetionarchitecture&#xff09;。  1945年&#xff0c;冯诺依曼首先提出了“存储程序”的概念和二进制原理&#xff0c;后来&#xff0c;人们把利用这种概念和原理设计的电子计算机系统统称为“冯诺依…

哈佛结构与冯诺依曼结构(含STM32系统结构解析)

存储器是微控制器的重要组成部分&#xff0c;不同类型的微控制器其采用的存储结构与容量不尽相同&#xff0c;但存储器的用途是相同的&#xff0c;用于存放程序和数据。微控制器中的存储结构有两种基本构成形式。 冯诺依曼结构 冯诺依曼结构也称普林斯顿结构&#xff0c;是一…

STM32属于哈佛结构还是冯诺依曼结构?

目录 01、冯诺依曼体系 02、哈佛体系 03、arm和哈佛、冯诺依曼的关系 04、实际芯片制造 现代的CPU基本上归为冯诺伊曼结构&#xff08;也成普林斯顿结构&#xff09;和哈佛结构。 冯洛伊曼结构就是我们所说的X86架构&#xff0c;而哈佛结构就是ARM架构。一个广泛用于桌面端…

哈佛体系结构

哈佛机&#xff1a;为数据和程序提供了格子独立的存储器。 程序计数器只指向程序存储器&#xff0c;而不指向数据存储器&#xff0c;这样的的后果是很难再哈佛机上编写出一个自修改的程序。独立的程序存储器和数据存储器为数字信号处理提供了较高的性能。结构如下图所示&#x…

哈佛结构和冯诺依曼结构?STM32属于哈佛结构还是冯诺依曼结构?

现代的CPU基本上归为冯诺伊曼结构&#xff08;也成普林斯顿结构&#xff09;和哈佛结构。 冯诺依曼体系 冯诺依曼体系结构图如下 冯诺依曼结构也称普林斯顿结构&#xff0c;是一种将程序指令存储器和数据存储器合并在一起的存储器结构。数据与指令都存储在同一存储区中&…

什么是冯诺依曼结构、哈佛结构、改进型哈佛结构?

冯诺依曼结构 冯诺依曼结构&#xff0c;又称为普林斯顿体系结构&#xff0c;是一种将程序指令存储器和数据存储器合并在一起的存储器结构。取指令和取操作数都在同一总线上&#xff0c;通过分时复用的方式进行&#xff1b;缺点是在高速运行时&#xff0c;不能达到同时取指令和取…

哈佛结构和冯诺依曼结构

已剪辑自: https://zhuanlan.zhihu.com/p/136748306 1946年&#xff0c;第一台计算机ENIAC诞生&#xff0c;人类进入计算机时代&#xff0c;后来&#xff0c;美籍匈牙利数学家&#xff1a;冯.诺依曼提出了计算机“存储程序”的计算机设计理念&#xff0c;即将计算机指令进行编码…

冯·诺依曼、哈佛、改进型哈佛体系结构解析

在如今的CPU中&#xff0c;由于Catch的存在&#xff0c;这些概念已经被模糊了。个人认为去区分他们并没有什么意义&#xff0c;仅作为知识点。 哈佛结构设计复杂&#xff0c;但效率高。冯诺依曼结构则比较简单&#xff0c;但也比较慢。CPU厂商为了提高处理速度&#xff0c;在C…

哈佛结构和冯·诺依曼结构

目录 一、哈佛结构 二、冯诺伊曼结构 三、哈佛结构和冯诺伊曼结构对比 一、哈佛结构 哈佛结构是一种将程序指令存储和数据存储分开的存储器结构。哈佛结构是一种并行体系结构&#xff0c;它的主要特点是将程序和数据存储在不同的存储空间中&#xff0c;即程序存储器和数据存…

哈佛架构和冯诺依曼架构

一、两种架构的介绍 1.哈佛结构是一种将程序指令的存储与数据的存储分开的存储器结构。首先&#xff0c;CPU在程序指令存储器中读取程序指令内容&#xff0c;解码后获得数据地址&#xff0c;然后在相应的数据存储器中读取数据&#xff0c;并进行下一步操作。指令存储和数据存储…

2021-05-28

嵌入式--深入理解单片机&#xff08;一&#xff09;单片机程序是如何运行起来的以及单片机的ROM和RAM 目录 一、两种处理器的结构体系 1、哈佛结构体系&#xff08;Harvard architecture&#xff09;2、冯诺依曼结构体系3、两种结构的总结 哈佛结构的优势冯诺依曼结构的优势当前…

冯诺依曼结构和哈佛结构

参考资料&#xff1a; 全面理解冯诺依曼结构和哈佛结构 CPU采用的是哈佛结构还是冯诺依曼结构&#xff1f; 0. 前言 哈佛结构和冯诺依曼结构都是针对于CPU来说的。 1. 冯诺依曼结构 冯诺伊曼结构又称为普林斯顿体系结构&#xff0c;是一种将程序存储器和数据存储器合并在一起…

哈佛结构和冯·诺依曼结构的区别

哈佛结构 (英语&#xff1a;Harvard architecture)是一种将程序指令储存和数据储存分开的存储器结构。中央处理器首先到程序指令储存器中读取程序指令内容&#xff0c;解码后得到数据地址&#xff0c;再到相应的数据储存器中读取数据&#xff0c;并进行下一步的操作&#xff08…

~isnan函数

一直不明白~的意思&#xff0c;现在实现一把 才知道表示非的意思

isNaN函数的使用方法

isNaN() 函数用于检查其参数是否是非数字值。