文章目录
- 🌳 数据结构与算法:深入理解哈夫曼树(最优二叉树)
- 1. 核心概念:什么是哈夫曼树?
- 2. 构造哈夫曼树及 WPL 计算
- 3. 经典例题解析
- 4. 总结
🌳 数据结构与算法:深入理解哈夫曼树(最优二叉树)
哈夫曼树(Huffman Tree),又称最优二叉树,是数据结构中非常经典的一种树形结构。它在数据压缩(如ZIP、JPEG)领域有着广泛的应用。本文将带你从定义到实战,彻底搞懂哈夫曼树。
1. 核心概念:什么是哈夫曼树?
在了解哈夫曼树之前,我们需要先理解几个基础术语:
- 路径:从树中一个节点到另一个节点之间的分支构成两个节点之间的路径。
- 路径长度:路径上的分支数目。
- 节点的权:树中节点被赋予的某种具有实际意义的数值(例如字符出现的频率)。
- 带权路径长度 (WPL):从根节点到该节点之间的路径长度与该节点上权的乘积。
哈夫曼树的定义:
给定n nn个权值作为n nn个叶子节点,构造一棵二叉树,若该树的带权路径长度 (WPL) 达到最小,则称这样的二叉树为最优二叉树,也称为哈夫曼树。
💡 核心特征:权值越大的叶子节点,距离根节点越近;权值越小的叶子节点,距离根节点越远。这样可以保证整体的 WPL 最小。
2. 构造哈夫曼树及 WPL 计算
构造哈夫曼树通常采用贪心算法的思想。
构造步骤(哈夫曼算法):
- 排序:根据给定的n nn个权值{ w 1 , w 2 , . . . , w n } \{w_1, w_2, ..., w_n\}{w1,w2,...,wn},构成n nn棵二叉树的集合F = { T 1 , T 2 , . . . , T n } F=\{T_1, T_2, ..., T_n\}F={T1,T2,...,Tn},其中每棵二叉树T i T_iTi中只有一个带权为w i w_iwi的根节点,其左右子树均为空。
- 选取:在F FF中选取两棵根节点权值最小的树作为左、右子树,构造一棵新的二叉树,且置新二叉树的根节点的权值为其左、右子树上根节点的权值之和。
- 删除与加入:从F FF中删除那两棵权值最小的树,同时将新得到的二叉树加入F FF中。
- 重复:重复步骤 2 和 3,直到F FF只含一棵树为止。这棵树便是哈夫曼树。
WPL 计算公式:
W P L = ∑ i = 1 n w i × l i WPL = \sum_{i=1}^{n} w_i \times l_iWPL=i=1∑nwi×li
其中w i w_iwi是第i ii个叶子节点的权值,l i l_ili是该叶子节点到根节点的路径长度。
3. 经典例题解析
让我们通过一道具体的题目来实战演练。
题目描述:
已知一个文件中出现的各字符及其对应的频率如下表所示。
| 字符 | a | b | c | d | e | f |
|---|---|---|---|---|---|---|
| 频率(权值) | 10 | 15 | 12 | 3 | 4 | 13 |
问题 1:若采用定长编码,则该文件中字符的码长应为多少?
问题 2:若采用Huffman 编码,则字符序列“face”的编码应为多少?
🔍 详细解析:
第一步:解决定长编码问题
- 分析:文件中共有 6 个不同的字符(a, b, c, d, e, f)。
- 计算:定长编码要求用相同长度的二进制位来区分所有字符。
- 1位二进制只能表示 2 个状态 (2 1 = 2 2^1=221=2)
- 2位二进制只能表示 4 个状态 (2 2 = 4 2^2=422=4)
- 3位二进制可以表示 8 个状态 (2 3 = 8 2^3=823=8)
- 结论:因为有 6 个字符,2位不够用,必须使用3位才能完全覆盖(例如 000 到 101)。
- 答案:3
第二步:构造哈夫曼树
我们需要根据频率 {10, 15, 12, 3, 4, 13} 构建树。
- 初始排序:d(3), e(4), a(10), c(12), f(13), b(15)
- 第一次合并:取最小的 d(3) 和 e(4),合并为新节点7。
- 剩余集合:{7, 10, 12, 13, 15}
- 第二次合并:取最小的 7 和 a(10),合并为新节点17。
- 剩余集合:{12, 13, 15, 17}
- 第三次合并:取最小的 c(12) 和 f(13),合并为新节点25。
- 剩余集合:{15, 17, 25}
- 第四次合并:取最小的 b(15) 和 17,合并为新节点32。
- 剩余集合:{25, 32}
- 第五次合并:合并 25 和 32,得到根节点57。
构造出的哈夫曼树结构示意(遵循左小右大原则):
[57] / \ [25] [32] / \ / \ c(12) f(13) b(15) [17] / \ [7] a(10) / \ d(3) e(4)第三步:生成编码
规则:通常规定哈夫曼树的左分支代表 0,右分支代表 1(反之亦可,但需统一)。
根据上面的树结构,各字符编码如下:
- f: 根 -> 左 -> 右 =01
- a: 根 -> 右 -> 右 -> 右 =111
- c: 根 -> 左 -> 左 =00
- e: 根 -> 右 -> 右 -> 左 -> 右 =1101
第四步:计算 “face” 的编码
将字符对应的编码拼接起来:
- f: 01
- a: 111
- c: 00
- e: 1101
结果:01111001101
(注:根据题目选项,可能存在左右子树分配0/1的不同习惯,或者合并时相等权值的处理顺序不同。根据你提供的参考图片和选项,若答案为B,则编码逻辑可能略有差异,但核心构造逻辑如上。让我们根据图片中的选项反推一下:)
- 图片中正确选项为B (001110110011)。
- 这说明在构建树时,可能采用了不同的左右分配策略(例如左1右0,或者合并顺序微调)。
- 但在考试或做题时,最通用的原则是:每次选两个最小的,小的放左边(0),大的放右边(1)。
4. 总结
- 定长编码:看字符个数N NN,码长L LL需满足2 L ≥ N 2^L \ge N2L≥N。
- 哈夫曼编码:
- 频率(权值)越高的字符,编码越短。
- 频率(权值)越低的字符,编码越长。
- 它是前缀编码,即任一字符的编码都不是另一个字符编码的前缀,保证了解码的唯一性。