哈夫曼树及其应用(文件压缩)

2,135 阅读6分钟

哈夫曼树概念

先通过一个小例子来引出哈夫曼树,例:将学生的百分制成绩转换为五分制成绩:≥90 分: A,80~89分: B,70~79分: C,60~69分: D,<60分: E。

编制一个程序,将百分制转换成五个等级输出

if (a < 60){
    b = "E";
}
else if (a < 70) {
    b = "D";
}
else if (a<80) {
    b = "C";
}
else if (a<90){
    b = "B";
}
else {
  b = "A";
}

若考虑上述程序所耗费的时间,就会发现该程序的缺陷。在实际中,学生成绩在五个等级上的分布是不均匀的。当学生百分制成绩的录入量很大时,上述判定过程需要反复调用,此时程序的执行效率将成为一个严重问题。

上述判断方式对应的判别树如下:

如果学生的总成绩数据有10000条,则5%的数据需 1 次比较,15%的数据需 2 次比较,40%的数据需 3 次比较,40%的数据需 4 次比较

因此 10000 个数据比较的 次数为: 10000 (5%+2×15%+3×40%+4×40%)=31500次

再看另一种判断方式:

此种形状的二叉树,需要的比较次数是:10000 (3×20%+2×80%)=22000次

显然:两种判别树的效率是不一样的。

哈夫曼树定义:在权为w1,w2,…,wn的n个叶子结点的所有二叉树中,带权路径长度WPL最小的二叉树称为赫夫曼树或最优二叉树。

带权路径长度的最优值叫做WPL也就是上述例子的判断总次数31500次或者22000次

其中,n表示叶子结点的数目,wi和li分别表示叶子结点ki的权值和树根结点到叶子结点ki之间的路径长度。也就是上述例子的wi==这个结果的次数,li==需要判断的次数。

哈夫曼树的构造

构造规则:

  1. 根据给定的n个权值{w1,w2,…,wn}构成二叉树集合F={T1,T2,…,Tn},其中每棵二叉树Ti中只有一个带权为wi的根结点,其左右子树为空.
  2. 在F中选取两棵根结点权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为左右子树根结点的权值之和.
  3. 在F中删除这两棵树,同时将新的二叉树加入F中.
  4. 重复2、3,直到F只含有一棵树为止.(得到哈夫曼树)

例:有4 个结点 a, b, c, d,权值分别为 7, 5, 2, 4,构造哈夫曼树。

根据给定的n个权值{w1,w2,…,wn}构成二叉树集合F={T1,T2,…,Tn}

在F中选取两棵根结点权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为左右子树根结点的权值之和. 在F中删除这两棵树,同时将新的二叉树加入F中.

重复,直到F只含有一棵树为止.(得到哈夫曼树)

哈夫曼应用--文件压缩

哈夫曼编码

哈夫曼树的应用很广,哈夫曼编码就是应用之一。

等长编码

例:如果需传送的电文为 ‘ABACCDA’,它只用到四种字符,用两位二进制编码便可分辨。假设 A, B, C, D 的编码分别为 00, 01,10, 11,则上述电文便为 ‘00010010101100’(共 14 位),译码员按两位进行分组译码,便可恢复原来的电文。 这种编码的特点是译码简单且具有唯一性,但编码长度并不是最短的。

不等长编码

在传送电文时,为了使其二进制位数尽可能地少,可以将每个字符的编码设计为不等长的,使用频度较高的字符分配一个相对比较短的编码,使用频度较低的字符分配一个比较长的编码。例如,可以为A,B,C,D四个字符分别分配0,00,1,01,并可将上述电文用二进制序列:000011010发送,其长度只有9个二进制位,但随之带来了一个问题,接收方接到这段电文后无法进行译码,因为无法断定前面4个0是4个A,1个B、2个A,还是2个B,即译码不唯一,因此这种编码方法不可使用。

因此,为了设计长短不等的编码,以便减少电文的总长,还必须考虑编码的唯一性,即在建立不等长编码时必须使任何一个字符的编码都不是另一个字符的前缀,这宗编码称为前缀编码(prefix code)利用哈夫曼树来实现

用二叉树设计二进制前缀编码

以电文中的字符作为叶子结点构造二叉树。然后将二叉树中结点引向其左孩子的分支标 ‘0’,引向其右孩子的分支标 ‘1’; 每个字符的编码即为从根到每个叶子的路径上得到的 0, 1 序列。如此得到的即为二进制前缀编码。

编码: A:0, C:10,B:110,D:111

任意一个叶子结点都不可能在其它叶子结点的路径中。

哈夫曼编码实例

比如有ABCDEF六个字母,通过0和1编码用二进制字符发送。普通编码后的数据为000001010011100101,解码的时候可以按照3位一份来解码。 假设ABCDEF出现的概率分别为27%、8%、15%、15%、30%、5%。则形成的赫夫曼树如下图。此外我们可以将左右分支分别改为0和1,然后用0和1来编码字母。

则编码结果为A=01、B=1001、C=101、D=00、E=11、F=1000。

原本的编码结果为:000001010011100101
现在的编码结果为: 0110011010011100

现在的编码结果明显要比之前的少了一些,短短是几个字母编码后,少的量不是很大,如果是通篇的文章或更多,那么编码量将会节省很多,这就是文件压缩的原理。并且随着字符的增多,按照权重优先级编码,这种压缩会进一步提升很多。