数据结构之树(Huffman tree(赫夫曼树 / 霍夫曼树 / 哈夫曼树 / 最优二叉树))
赫夫曼树概述
HuffmanTree因翻译不同导致其有多个名字:赫夫曼树、霍夫曼树、哈夫曼树
赫夫曼树又称最优二叉树,是一种带权路径长度 最短的二叉树。
所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。
树的路径长度是从树根到每一结点的路径长度之和,记为WPL=(W1L1+W2L2+W3L3+…+WnLn),N个权值Wi(i=1,2,…n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,…n)。可以证明赫夫曼树的WPL是最小的。
术语
1. 路径
路径是指从一个节点到另一个节点的分支序列。
2. 路径长度
指从一个节点到另一个结点所经过的分支数目。 如下图:从根节点到a的分支数目为2
3. 树的路径长度
树中所有结点的路径长度之和为树的路径长度PL。 如下图:PL为10
根节点到A的父节点为1
根节点到C的父节点为1
根节点到A节点为2
根节点到B节点为2
根节点到C节点为2
根节点到D节点为2
4. 节点的权
给树的每个结点赋予一个具有某种实际意义的实数,我们称该实数为这个结点的权。如下图:7、5、2、4
5. 带权路径长度
从树根到某一结点的路径长度与该节点的权的乘积,叫做该结点的带权路径长度。如下图:A的带权路径长度为2*7=14
6. 树的带权路径长度(WPL)
树的带权路径长度为树中所有叶子节点的带权路径长度之和
7. 最优二叉树
权值最大的节点离根节点越近的二叉树,所得WPL值最小,就是最优二叉树。如下图:(b)A节点权值最大(为9)且离根节点最近
- (a)
WPL=9*2+4*2+5*2+2*2=40
- (b)
WPL=9*1+5*2+4*3+2*3=37
构造赫夫曼树步骤
对于数组{5,29,7,8,14,23,3,11},把它构造成赫夫曼树。
第1步:使用数组中所有元素创建若干个二叉树节点,这些值作为节点的权值。
第2步:将这些节点按照权值的大小进行排序
第3步:取出权值最小的两个节点,并创建一个新的节点作为这两个节点的父节点,这个父节点的权值为两个子节点的权值之和。将这两个节点分别赋给父节点的左右节点。
第4步:删除这两个节点,将父节点添加进集合里
第5步:重复第二步到第四步,直到集合中只剩一个元素,结束循环
代码实现
1 import java.util.ArrayList; 2 import java.util.Collections; 3 import java.util.List; 4 5 /** 6 * 霍夫曼树 7 */ 8 public class TestHuffmanTree { 9 public static void main(String[] args) { 10 int[] arr = {5, 29, 7, 8, 14, 23, 3, 11}; 11 Node node = createHuffmanTree(arr); 12 System.out.println(node); //Node value=100 13 } 14 15 //创建赫夫曼树 16 public static Node createHuffmanTree(int[] arr) { 17 //使用数组中所有元素创建若干个二叉树(只有一个节点) 18 List<Node> nodes = new ArrayList<>(); 19 for (int value : arr) { 20 nodes.add(new Node(value)); 21 } 22 23 //循环处理 24 while (nodes.size() > 1) { 25 //排序 26 Collections.sort(nodes); 27 //取出最小的两个二叉树(集合为倒叙,从大到小) 28 Node left = nodes.get(nodes.size() - 1); //权值最小 29 Node right = nodes.get(nodes.size() - 2); //权值次小 30 //创建一个新的二叉树 31 Node parent = new Node(left.value + right.value); 32 //删除原来的两个节点 33 nodes.remove(left); 34 nodes.remove(right); 35 //新的二叉树放入原来的二叉树集合中 36 nodes.add(parent); 37 //打印结果 38 System.out.println(nodes); 39 } 40 return nodes.get(0); 41 } 42 } 43 44 //接口实现排序功能 45 class Node implements Comparable<Node> { 46 int value; 47 Node left; 48 Node right; 49 50 public Node(int value) { 51 this.value = value; 52 } 53 54 @Override 55 public int compareTo(Node o) { 56 return -(this.value - o.value); //集合倒叙,从大到小 57 } 58 59 @Override 60 public String toString() { 61 return "Node value=" + value; 62 } 63 }
输出:
1 2 3 4 5 6 7 8 | [Node value= 29 , Node value= 23 , Node value= 14 , Node value= 11 , Node value= 8 , Node value= 7 , Node value= 8 ] [Node value= 29 , Node value= 23 , Node value= 14 , Node value= 11 , Node value= 8 , Node value= 15 ] [Node value= 29 , Node value= 23 , Node value= 15 , Node value= 14 , Node value= 19 ] [Node value= 29 , Node value= 23 , Node value= 19 , Node value= 29 ] [Node value= 29 , Node value= 29 , Node value= 42 ] [Node value= 42 , Node value= 58 ] [Node value= 100 ] Node value= 100 |
应用场景
赫夫曼树的典型应用场景是在数据压缩和编码中,尤其是在通信和存储领域。以下是赫夫曼树的典型应用场景:
-
数据压缩:赫夫曼树广泛用于数据压缩算法,例如在ZIP、Gzip、JPEG、MP3等压缩文件格式中。数据压缩通过使用赫夫曼编码来减小数据的体积,从而节省存储空间和提高数据传输效率。频率高的字符或数据被编码为较短的比特串,而频率低的字符或数据被编码为较长的比特串。
-
通信传输:在通信领域,赫夫曼编码可用于压缩数据以减小传输带宽的需求。这在网络通信、移动通信和卫星通信中非常重要,因为它可以减少数据传输的成本并提高传输速度。HTTPS通信中的数据也经常使用赫夫曼编码进行加密和压缩。
-
图像压缩:JPEG图像压缩标准使用了一种称为"霍夫曼熵编码"的方法,其中赫夫曼编码用于压缩图像数据,减小图像文件的大小,同时保持图像质量。
-
音频压缩:音频压缩算法如MP3也使用赫夫曼编码来减小音频文件的大小,同时保持音质。这使得音频文件更适合在互联网上流式传输和存储。
-
文件压缩:文件压缩工具如ZIP和Gzip使用赫夫曼编码来压缩多种类型的文件。这些工具可以将多个文件或文件夹打包成一个单一的压缩文件,以节省存储空间和方便文件传输。
-
数据存储:在一些数据库管理系统中,赫夫曼编码用于优化数据的存储和检索。赫夫曼编码可以减小存储数据所需的空间,同时提高数据检索的效率。
-
数据传输的纠错:在一些通信协议中,赫夫曼编码还可以用于检测和纠正传输中的错误。这有助于提高数据传输的可靠性。
总之,赫夫曼树和赫夫曼编码在多个应用领域中都发挥了关键作用,减小了数据传输和存储的开销,提高了效率,并保持了数据的完整性和质量。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)