本讲定位:官方大纲把「基于树的编码」单列为知识块 2,真题给的分量也很稳定——单选第 9、10、11 题连续三道(哈夫曼 WPL、哈夫曼树结点数、格雷码性质),判断题再出 1~2 条,合计 6~10 分。
这一讲的特点是:全部靠手算,没有编程题。会画一棵哈夫曼树、会套两个公式、会背两条格雷码性质,分就拿到了。是六级仅次于六级第 01 讲的性价比之选。
对应官方大纲:六级知识块 1「树」——哈夫曼树;知识块 2「基于树的编码」——格雷编码、哈夫曼编码。
前置知识:六级第 02 讲(二叉树、遍历)、五级第 08 讲(贪心)。
代码说明:本讲代码均已用 g++ 11.5(
-std=c++11)编译运行验证;WPL 程序与手算结果逐例核对,哈夫曼编码总长与真题给定答案一致。
问题:要给一批字符编二进制码,出现次数多的字符用短码,次数少的用长码,让编码后的总长度最短。
这是一个贪心问题(五级第 08 讲讲过贪心的思路)。哈夫曼给出的贪心策略只有一句话:
$$ \boxed{\textbf{每次取出当前最小的两个数,合并成一个新数(它们的和),放回去;重复到只剩一个}} $$
例:权值 $\{1, 2, 3, 4, 5, 6, 7, 8\}$。
| 轮次 | 取出的两个最小值 | 合并成 | 剩下的集合 |
|---|---|---|---|
| 1 | 1, 2 | 3 | {3, 3, 4, 5, 6, 7, 8} |
| 2 | 3, 3 | 6 | {4, 5, 6, 6, 7, 8} |
| 3 | 4, 5 | 9 | {6, 6, 7, 8, 9} |
| 4 | 6, 6 | 12 | {7, 8, 9, 12} |
| 5 | 7, 8 | 15 | {9, 12, 15} |
| 6 | 9, 12 | 21 | {15, 21} |
| 7 | 15, 21 | 36 | {36} ← 只剩一个,结束 |
⚠️ 第 2 轮的坑:合并出来的
3要放回集合里参与后续比较,而且它和原来的3地位完全一样。很多学生会一直只盯着原始数据挑,这是最常见的错误。
$$ \text{WPL} = \sum_{\text{每个叶子}} (\text{权值} \times \text{该叶子的深度}) $$
深度按「根到叶子的边数」算(根的深度为 0)。
但考场上没人这么算。 用下面这个捷径:
$$ \boxed{\textbf{WPL} = \textbf{所有合并操作产生的新值之和}} $$
上例:$3+6+9+12+15+21+36 = 102$。
📌 为什么这个捷径成立? 每合并一次,参与合并的那部分权值就往下沉一层,深度各加 1,对 WPL 的贡献就恰好增加「它们的和」。所以把每次合并的和累加起来,就是 WPL。
这个捷径要让学生彻底吃透:手算 WPL 完全不用画树,列个表累加即可,三十秒出答案。
2026 年 3 月六级单选第 9 题:6 个字符,出现次数分别为 $\{2, 3, 3, 4, 6, 8\}$,求最小 WPL。