哈夫曼编码,编码过程:
编码过程:哈夫曼编码是一种变长编码方法,用于数据压缩等场景,具体步骤如下:
首先,将字符出现的概率当作权值,构建哈夫曼树。这一步是基础,通过不断选取权值最小的两个节点合并,逐步生成树结构。
接着,按照 “左 0 右 1” 的规则给哈夫曼树的每条边标注。
然后,从根节点出发,到每个叶节点的 0 和 1 的组合,就成为该叶节点对应字符的编码。
最终,通过这样的方式得到各个字符的哈夫曼编码,能让出现概率高的字符获得较短编码,概率低的字符获得较长编码,从而实现数据压缩等目的。
题目一: 字母表 {a, b, c, d, e} 的频率分别为 10%,15%,30%,16%,29%。若使用哈夫曼编码方式对字母进行不定长的二进制编码,给出a、b、c、d、e分别对应的一组哈夫曼编码
题目二: 字符 {a, b, c, d, e, f} 的频率分别为 5%, 9%, 12%, 13%, 16%, 45%。请问 a、b、c、d、e、f 分别对应的一组哈夫曼编码
下面给出两道题的 完整哈夫曼编码过程、一步步合并(带图)以及最终编码和平均码长计算。注意:哈夫曼编码在合并权值相同时并非唯一——我给出的是一种常见且清晰的构造(把合并时的左子记为 0、右子记为 1),你也可以通过其他等价的 tie-breaking 得到不同但等价的编码。
题目一
字母表 {a,b,c,d,e} 的频率分别为 a:10%, b:15%, c:30%, d:16%, e:29%。
目标:构造哈夫曼树并给出每个字母的二进制编码。
合并步骤(按权值从小到大合并两个最小)
初始节点(按频率从小到大列出):
a(10), b(15), d(16), e(29), c(30)
- 合并
a(10)和b(15)→ 新节点ab(25)。(我们把a放左子记0,b放右子记1) - 此时节点:
d(16), ab(25), e(29), c(30)。合并最小的d(16)与ab(25)→ 新节点d-ab(41)。(d左0,ab右1) - 节点变为:
e(29), c(30), d-ab(41)。合并e(29)与c(30)→ 新节点e-c(59)。(e左0,c右1) - 最后合并
d-ab(41)与e-c(59)→ 根节点100。
对应的哈夫曼树(ASCII 示意)
(100)
/ \
(41) (59)
/ \ / \
d(16) (25) e(29) c(30)
/ \
a(10) b(15)
(每条左边分支记 0,右边分支记 1)
从根到叶得到编码
d:根 → 左 (41) → 左 (d) ⇒00a:根 → 左 (41) → 右 (25) → 左 (a) ⇒010b:根 → 左 (41) → 右 (25) → 右 (b) ⇒011e:根 → 右 (59) → 左 (e) ⇒10c:根 → 右 (59) → 右 (c) ⇒11
| 字符 | 频率 | 编码 |
|---|---|---|
| a | 10% | 010 |
| b | 15% | 011 |
| c | 30% | 11 |
| d | 16% | 00 |
| e | 29% | 10 |
平均码长计算(逐项算,确保精确)
平均码长 \(L=\sum p_i \times \ell_i\):
- \(a\): \(0.10\times 3 = 0.30\)
- \(b\): \(0.15\times 3 = 0.45\)
- \(c\): \(0.30\times 2 = 0.60\)
- \(d\): \(0.16\times 2 = 0.32\)
- \(e\): \(0.29\times 2 = 0.58\)
合计 \(L = 0.30+0.45+0.60+0.32+0.58 = 2.25\)(比固定等长编码更短,说明压缩效果)
题目二
字符 {a,b,c,d,e,f} 的频率分别为 a:5%, b:9%, c:12%, d:13%, e:16%, f:45%。
目标:给出一组哈夫曼编码并详细分析合并过程。
合并步骤
初始按频率从小到大: a(5), b(9), c(12), d(13), e(16), f(45)
- 合并
a(5)与b(9)→ab(14)。(a左0,b右1) - 节点变为:
ab(14), c(12), d(13), e(16), f(45)。合并c(12)与d(13)→cd(25)。(c左0,d右1) - 节点:
ab(14), cd(25), e(16), f(45)。合并ab(14)与e(16)→abe(30)。(ab左0,e右1) - 节点:
cd(25), abe(30), f(45)。合并cd(25)与abe(30)→cd-abe(55)。(cd左0,abe右1) - 最后合并
cd-abe(55)与f(45)→ 根100。
哈夫曼树(示意)
(100)
/ \
(55) f(45)
/ \
(25) (30)
/ \ / \
c(12)d(13) (14) e(16)
/ \
a(5) b(9)
从根到叶得到编码
-
f:直接右子 ⇒1(频率最大,码最短) -
在左子子树(以
0开头):c:0 0 0⇒000d:0 0 1⇒001e:0 1 1⇒011a:0 1 0 0⇒0100b:0 1 0 1⇒0101
| 字符 | 频率 | 编码 |
|---|---|---|
| a | 5% | 0100 |
| b | 9% | 0101 |
| c | 12% | 000 |
| d | 13% | 001 |
| e | 16% | 011 |
| f | 45% | 1 |
平均码长计算
逐项计算(以小数表示概率):
- \(a\): \(0.05\times 4 = 0.20\)
- \(b\): \(0.09\times 4 = 0.36\)
- \(c\): \(0.12\times 3 = 0.36\)
- \(d\): \(0.13\times 3 = 0.39\)
- \(e\): \(0.16\times 3 = 0.48\)
- \(f\): \(0.45\times 1 = 0.45\)
合计 \(L = 0.20+0.36+0.36+0.39+0.48+0.45 = 2.24\)(平均码长 2.24 bit)
额外说明(常见疑问)
- 唯一性:哈夫曼编码并不唯一。当多个节点频率相同或在排序时选择左右子顺序不同,会得到不同但等价的编码(平均码长相同)。上面两题给的是一种明确的 tie-breaking 规则(每次合并后把较小的放左或按我示例中的顺序),所以结果确定且便于说明。
- 前缀无冲突:哈夫曼编码是前缀码(prefix-free),即任一编码都不是另一个编码的前缀,便于无歧义地解码(树的叶子均为符号,非叶子无符号)。你可以从树结构直观验证:每个符号对应树的一个叶节点。
- 解码方法:读一串比特,从根出发按
0/1走到叶子就得到了一个符号,然后回到根继续读后续比特。 - 最优性:哈夫曼算法在满足符号独立、按符号概率固定且按比特长度计成本时,能得到最小平均长度的前缀码(贪心最优)。

浙公网安备 33010602011771号