一、哈夫曼树的定义
在实际应用中,树中结点常常被赋予一个表示某种意义的数值,称为该结点的权。
从树根结点到任意结点的路径长度(经过的边数)与该结点上权值的乘积,称为该结点的带权路径长度。
树中所有叶结点的带权路径长度之和称为该树的带权路径长度,记为
WPL=
式中,是第i个叶结点所带的权值,是该叶结点到根结点的路径长度。
在含有n个带权叶子结点的二叉树中,其中带权路径长度(WPL)最小的二叉树称为哈夫曼树,也称为最优二叉树。哈夫曼树一定是一颗二叉树。
举个栗子:如下图3个二叉树都有4个叶子结点a,b,c,d,分别带权7,5,2,4,它们的带权路径长度分别为:
(a)WPL=7*2+5*2+2*2+4*2=36;
(b)WPL=7*3+5*3+2*1+4*2=46;
(c)WPL=7*1+5*2+2*3+4*3=35。
其中,c树的WPL最小,可以验证,其为哈夫曼树。
二、哈夫曼树的构造
给定n个权值分别为的结点,通过哈夫曼算法可以构造出最由二叉树,算法描述如下:
1)将这n个结点分别作为n棵仅含一个结点的二叉树,构成森林F。
2)构造一个新结点,从F中选取两棵根结点权值最小的树作为新结点的左、右子树,并且将新结点的权值置为左、右子树上根结点的权值之和。
(这里的权值在实际中为编码频率,见哈夫曼编码)
3)从F中删除刚才选出的两棵树,同时将新得到的树加入F中。
4)重复步骤2)和3),直至F中只剩下一棵树为止。
可以看出哈夫曼树权值越小的结点到根结点的路径长度越大。
其建造过程如下图所示,权值{7,5,2,4}
三、哈夫曼编码(哈夫曼的重要应用)
对于待处理的一个字符串序列,若对每个字符用同样长度的二进制位表示,则称这种编码方式为固定长度编码。
若允许对不同字符用不等长的二进制位表示,则这种方式称为可变长度编码。可变长度编码比固定长度编码好得多,其特点是对频率高的字符赋以短编码,而对频率较低的字符则赋以较长一些的编码,从而可以使字符平均编码长度减短,起到压缩数据的效果。
哈夫曼编码是一种被广泛应用而且非常有效的数据压缩编码。
若没有一个编码是另一个编码的前缀,则称这样的编码称为前缀编码。如0、101和100是前缀编码。对前缀编码的解码很简单,因为没有一个码是其他码的前缀。所以识别出第一个编码,将它翻译为原码,再对余下的编码文件重复同样的解码操作。例如,00101100可被唯一地分析为0,、0、101和100。
由哈夫曼树得到哈夫曼编码是很自然的过程。首先,将每个出现的字符当作一个独立的结点,其权值为它出现的频度(或次数),构造出对应的哈夫曼树。
显然,所以字符结点都出现在叶结点中。我们可将字符的编码解释为从根至该字符的路径上边标记的序列,其中边标记为0表示“转向左孩子”,标记为1表示“转向右孩子”(左0右1)。
如下图所示为一个由哈夫曼树构造哈夫曼编码的示例,矩形方块表示字符及其出现的次数。
这棵哈夫曼树的WPL为
WPL=1*45+3*(13+12+16)+4*(5+9)=224
此处的WPL可视为最终编码得到二进制编码的长度,共224位。若采用3位固定长度编码,则得到的二进制编码长度为300位,因此哈夫曼编码共压缩了25%的数据。利用哈夫曼树可以设计出总长度最短的二进制前缀编码。