哈夫曼树
定义
- 定义:带权路径长度WPL最小的二叉树称作哈夫曼树,又叫最优二叉树
- 节点的带权路径长度为:从该节点到树根之间的路径长度与节点上的权的乘积
- 树的带权路径长度为:所有叶子节点的带权路径长度之和
构造方式
大话数据结构:
-
根据给定的n个权值{ w1,w2,w3,···,wn }构成n棵二叉树的集合F = { T1,T2,T3,···,Tn },其中每棵二叉树Ti中只有一个带权为wi的根节点,其左右子树均为空。(其实就是一个节点)
-
在F中选取两棵根节点的权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根节点的权值为其左右子树上根节点的权值之和。
-
在F中删除这两棵树,同时将新得到的二叉树加入F中。
-
重复步骤2和3,直到F只含一棵树为止,这棵树便是哈夫曼树。(这个节点便是哈夫曼树的根节点)
举个例子:

现在有五个字符ABCDE,权值分别为5, 15, 40, 30, 10
先取出权值最小的两个字符,分别为 A, E
将它们作为左右子树构造一棵新的二叉树:
再将A, E从集合F中删除,将新的得到的N1加入到F中:
N1的权值即为A, E的权值之和:5 + 10 = 15

重复上述步骤,将权值最小的N1和B作为左右子树构建一棵新的二叉树:
重复上述步骤,将N2加入到集合F中,将N1和B从集合F中删除,选取F中权值最小的两个节点分别为N2, D构建新的二叉树:
删除N2, D,添加N3,选取最小的两个节点,C和N3作为左右子树构建二叉树:
这样,就完成了哈夫曼树的构造,可以算得:
\(WPL = 5 * 3 + 15 * 3 + 40 * 2 + 30 * 2 + 10 * 2 = 220\)
哈夫曼编码
将字符的频率作为权值来构建好的哈夫曼树,规定左分支为 0,右分支为 1,则从根节点到叶子节点所经过的路径分支组成的 01序列便为该节点对应字符的编码,这就是哈夫曼编码
将上述的哈夫曼树作为例子,设我们要传输的信息就只有ABCDE这五个字符。
设字符A的频率为5%,B为15%,C为40%,D为30%,E为10%。
假设我们不用哈夫曼编码,很自然的想到,我们用二进制来表示这5个字符:

现在有一段文本内容:CADECDDBACE
二进制表示:010000011100010011011001000010100
哈夫曼编码:01000111001000101100001001
很明显,用哈夫曼编码传送数据,节约了存储。
若编码为长短不等,那么必须任一字符的编码都不是另一个字符的编码的前缀,这种编码叫前缀编码。
仔细观察就会发现,哈夫曼编码不会出现混淆的情况,这是因为每个字符都是在树上的叶子节点上。
实验
实验简介
实验项目: 树形结构及其应用
实验题目: 哈夫曼编码与译码方法
实验内容:
哈夫曼编码是一种以哈夫曼树(最优二叉树,带权路径长度最小的二叉树)为基础变长编码方法。其基本思想是:将使用次数多的代码转换成长度较短的编码,而使用次数少的采用较长的编码,并且保持编码的唯一可解性。在计算机信息处理中,经常应用于数据压缩。是一种一致性编码法(又称"熵编码法"),用于数据的无损压缩。要求实现一个完整的哈夫曼编码与译码系统。
实验要求:
- 从文件中读入任意一篇英文文本文件,分别统计英文文本文件中各字符(包括标点符号和空格)的使用频率;
- 根据已统计的字符使用频率构造哈夫曼编码树,并给出每个字符的哈夫曼编码(字符集的哈夫曼编码表);
- 将文本文件利用哈夫曼树进行编码,存储成压缩文件(哈夫曼编码文件);
- 计算哈夫曼编码文件的压缩率;
- 将哈夫曼编码文件译码为文本文件, 并与原文件进行比较。
测试结果
test.txt 英文文本测试文件 test_Copy.txt 英文文本解码文件
code.txt 哈夫曼编码文件 huffman.txt 哈夫曼树的结构文件
一开始,只需要test.txt,内容如下
Hello,I am Az1r!
I come from China.
Now I am a student,and I major in Computer Science.
编码,译码,打印出哈夫曼编码表:

代码
点击查看代码
/*
Author: Az1r
Date: 2022/10/14
*/
#include
#include
#include
#include
#include
#include
一些相关的问题
- 我们现在是将哈夫曼编码同样作为文本文件存储在.txt文件中,如何将这些01序列直接存储到二进制文件或者是压缩文件呢?1个字符在文本文件中是占1个字节,占8位,我们现在将1个字符转为多个字符(都是01)表示,事实上,是扩大了存储空间。
- 代码中的堆,是手写的小根堆,也可以使用STL中优先队列priority_queue来实现。
- 上述 1-5 的编码和译码是基于字符的压缩,如何考虑基于单词的压缩,完成上述工作。
- 上述 1-5 的编码是二进制的编码,如何采用 K 叉的哈夫曼树完成上述工作,实现“K 进制”的编码和译码 。
参考资料
-
程杰. 大话数据结构:溢彩加强版[M]. 北京: 清华大学出版社, 2020.
-
[数据结构] 使用最小堆思想实现哈夫曼编解码