1. 哈夫曼树的基本概念
哈夫曼树( Huffman )又称最优二叉树,是一类带权路径长度最短的树,有着广泛的应用。
在讨论哈夫曼树之前首先需要弄清楚关于路径和路径长度的概念。树中两个结点之间的路径由一个结点到另一结点的分支构成。两结点之间的路径长度是路径上分支的数目。树的路径长度是从根结点到每一个结点的路径长度之和。
设一棵二叉树有 n 个叶子结点,每个叶子结点拥有一个权值W 1 ,W 2 , ...... W n ,从根结点到每个叶子结点的路径长度分别为 L1 , L2......Ln ,那么树的带权路径长度为每个叶子的路径长度与该叶子权值乘积之各。通常记作 WPL = L k. W k 。为了直观其见,在图中把带权的叶子结点画成方形,其他非叶子结点仍为圆形。请看图 6.21 中的三棵二叉树以及它们的带权路径长。
(a) wpl=38 (b) wpl=49 (c) wpl=36
图 6.21 具有不同带权路径长度的二叉树
注意:
这三棵二叉树叶子结点数相同,它们的权值也相同,但是它们的 wpl 带权路径长各不相同。图 6.21(c)wpl 最小。它就是哈曼树,最优树。哈夫曼树是,在具有同一组权值的叶子结点的不同二叉树中,带权路径长度最短的树。也称最优树。
2. 哈夫曼树的构造
构造哈夫曼树的方法(贪婪方法)
对于已知的一组叶子的权值W 1 ,W 2...... ,W n
①首先把 n 个叶子结点看做 n 棵树(仅有一个结点的二叉树),把它们看做一个森林。
②在森林中把权值最小和次小的两棵树合并成一棵树,该树根结点的权值是两棵子树权值之和。这时森林中还有 n-1 棵树。
③重复第②步直到森林中只有一棵为止。此树就是哈夫曼树。现给一组 (n=4) 具体的权值 2 , 4 , 5 , 8 ,下边是构造具体过程:
图 6.22 哈夫曼树构造过程
图 6.22(a) 是一个拥有 4 棵小树的森林,图 6.22(b) 森林中还有 3 子棵树,图 6.22(c) 森林中剩下 2 棵树,图 6.22(d) 森林只有一棵树,这棵树就是哈夫曼树。这里或许会提出疑问, n 个叶子构成的哈夫曼树其带权路径长度唯一吗?确实唯一。树形唯一吗?不唯一。因为将森林中两棵权值最小和次小的子棵合并时,哪棵做左子树,哪棵做右子树并不严格限制。图 6.22 之中的做法是把权值较小的当做左子树 , 权值较大的当做右子树。如果反过来也可以,画出的树形有所不同,但 WPL 值相同。为了便于讨论交流在此提倡权值较小的做左子树 , 权值较大的做右子。
还有一点就是当外结点和内结点权值相同时,应该把内结点放在后面,即让外节点先构成树,这样能使得最后的树“最矮”(最长路径尽可能短)。
例子:对3,4,5,7构造哈弗曼树。
3. 哈夫曼树的应用
(1) 用于最佳判断过程。
在考查课记分时往往把百分制转换成优( x>=90 )、良 (80<x<90) 、中 (70<=x80) 、及格 (60<=x<70) 、不及格 (x<60) 五个等级。若不考虑学生考试分数的分布概率,程序判定过程很容易写成图 6.23(a) 所示的方法。我们知道,一般来讲学生考分大多分布在 70 至 80 分之间,从图 6.23(a) 可看出这种情的 x 值要比较 2 至 3 次才能确定等级。而学生中考试不及格的人数很少, x 值比较一次即可定等级。能否使出现次数多的在 70 至 80 分之间的 x 值比较次数减少些,而使很少出现的低于 60 分的 x 值比较次数多一些,以便提高程序的运行效率呢?假设学生成绩对于不及格、及格、中等、良好和优秀的分布概率分别为 5 %, 15 %, 40 %, 30 %, 10 %,以它们做为叶子的权值来构造哈夫曼树,如图 6.23(b) 所示。此时带权路径长最短,其值为 205 %。它可以使大部分的分数值经过较少的比较次数得到相应的等级。但是,事务往往不是绝对的,此时每个判断柜内的条件较为复杂,比较两次,反而降低运行效率。所以我们采用折衷作法,调整后得图 6.23(c) 判定树。它更加切合实际。
(2) 用于通信编码
在通信及数据传输中多采用二进制编码。为了使电文尽可能的缩短,可以对电文中每个字符出现的次数进行统计。设法让出现次数多的字符的二进制码短些,而让那些很少出现的字符的二进制码长一些。假设有一段电文,其中用到 4 个不同字符A,C,S,T,它们在电文中出现的次数分别为 7 , 2 , 4 , 5 。把 7 , 2 , 4 , 5 当做 4 个叶子的权值构造哈夫曼树如图 6.24(a) 所示。在树中令所有左分支取编码为 0 ,令所有右分支取编码为1。将从根结点起到某个叶子结点路径上的各左、右分支的编码顺序排列,就得这个叶子结点所代表的字符的二进制编码,如图 6.24(b) 所示。
这些编码拼成的电文不会混淆,因为每个字符的编码均不是其他编码的前缀,这种编码称做前缀编码。
关于信息编码是一个复杂的问题,还应考虑其他一些因素。比如前缀编码每个编码的长度不相等,译码时较困难。还有检测、纠错问题都应考虑在内。这里仅对哈夫曼树举了一个应用实例。