Huffman树和Huffman编码
Posted 薛定谔的猫ovo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Huffman树和Huffman编码相关的知识,希望对你有一定的参考价值。
Huffman树的定义
哈夫曼(Huffman)树,又称最优二叉树,是一类带权路径长度WPL最短的树。
带权路径长度WPL
要理解带权路径长度的内涵,首先需要了解的一些概念:
- 路径:从树中一个结点到另一个结点之间的分支构成这两个结点之间的路径。
- 路径长度:路径上的分支数目。即经过的边数。
- 树的路径长度:从树根结点到每一个结点的路径长度之和。
- 结点的权:结点被赋予的一个表示某种意义的数值称为该结点的权。
那么,树的带权路径长度(Weighted Path Length of Tree):树中所有叶子结点的带权路径长度之和,通常记作
W
P
L
=
∑
i
=
1
n
w
i
l
i
WPL=\\sum_{i=1}^n{w_i}{l_i}
WPL=i=1∑nwili
其中
w
i
w_i
wi为第i个叶子所带的权值,
l
i
l_i
li为该叶结点到根结点的路径长度。
即WPL = ∑ ( 每个叶结点的权值 * 叶结点到根结点的路径长度 )
如下图所示,分别求三棵树的WPL:
(a)WPL = 7 × 2 + 5 × 2 + 2 × 2 + 4 × 2 = 36.
(b)WPL = 4 × 2 + 7 × 3 + 5 × 3 + 2 × 1 = 46.
(c)WPL = 7 × 1 + 5 × 2 + 2 × 3 + 4 × 3 = 35.
其中,图(c)中树的WPL最小,可以验证它恰好为Huffman树。
Huffman树的构造
给定n个权值分别为 w 1 w_1 w1, w 2 w_2 w2,… , w n w_n wn的结点,通过Huffman算法构造出最优二叉树,算法描述如下:
<1>将这n个结点分别作为n棵只含有一个结点的二叉树,构成森林F。
<2>构造一个新结点,从F中选取两棵根结点权值最小的树作为新结点的左、右子树,并且将新结点的权值置为左、右子树上根结点的权值之和。
<3>从F中删除刚才选出的两棵树,同时将新得到的树加入F中。
<4>重复<2>和<3>步骤,直至F中只剩下一棵树为止。
这样只看算法比较抽象,下面给出一个构造Huffman树的实例来帮助读者更好的理解和掌握Huffman构造的算法。
权值{7,5,2,4}的Huffman树的构造过程如下:
<1>将这四个权值赋予四个结点,构成森林F。
<2>构造一个新结点,选取根结点权值最小的两棵树c和d作为新结点的左、右子树,将新结点的权值置为c和d的权值之和6.
<3>从F中删除c和d两个结点,并将新得到的树加入F.
<4>构造一个新结点,从新F中选取根结点权值最小的两棵树,即权值为5和6的两棵树作为新结点的左、右子树,并将新结点的权值置为11.
<5>从F中删掉根结点权值为5和6的树,并将新得到的树加入到F中.
<6>此时F中只剩下两棵树,则再构建一个新结点,将这两棵树作为新结点的左、右子树,并且将新结点的权值置为18.
至此,F中只剩下一棵树,即为构造的对应的Huffman树。
Huffman树的特点
从Huffman树的构造过程可以看出,Huffman树具有以下特点:
1、每个初始结点最终都成为叶结点,而且权值越小的结点到根结点的路径长度越大。
2、构造过程中一共创建了n-1个结点(双分支结点),因此Huffman树中的结点总数为2n-1.
3、每次构造都选择2棵树作为新结点的孩子,因此Huffman树中不存在度为1的结点。
Huffman编码
首先给出几个概念:固定长度编码、可变长度编码、前缀编码。
- 固定长度编码:对于待处理的一个字符串序列,若对每个字符用同样长度的二进制位表示,则称这种编码方式为固定长度编码。
- 可变长度编码:若允许对不同的字符用不等长的二进制位表示,则这种方式成为可变长度编码。
- 前缀编码:若没有一个编码是另一个编码的前缀,则称这样的代码为前缀编码。
目前,进行快速远距离通信的主要手段是电报,即将需要传送的文字转换成由二进制的字母组成的字符串。
1、关于固定长度编码,举一个简单的小例子:
假设需要传送的字符为’A B A C C D A’,它只有四种字符,只需要两位二进制码即可分辨。
设A、B、C、D的编码分别为00、01、10和11,则上述字符串的电文为’00010010101100’,总长为14位。
对方在接收时,可按二位一分进行译码。
2、当然,在传送电文时,希望总长度尽可能的短,如果对每个字符设计长度不等的编码,且让电文中出现次数较多的字符采用尽可能短的编码,则传送电文的总长便可减少。这就是可变长度编码:
如果设A、B、C、D的编码分别为0、00、1、01,则上述7个字符的电文可转换成’00001010’,总长度为9。
但是,这样的电文无法翻译,例如传送过去的字符串中前4个字符的子串’0000’就可以有多种译法,比如’AAAA’、‘ABA’、'BB’等等。
3、因此,若要设计长短不等的编码,则必须是任一个字符的编码都不是另一个字符的编码的前缀,这种编码就叫做前缀编码。
对前缀编码的解码很简单,因为没有一个码是其他码的前缀。所以,可以识别出第一个编码,将它翻译为原码,再对余下的编码重复同样的解码操作。
例如,0、101、100是前缀编码。那么00101100可以被唯一地分析为0、0、101和100。
那么如何设计前缀编码呢?可以利用二叉树来设计二进制的前缀编码。
例如下图,其4个叶子结点分别表示A、B、C、D这四个字符,且约定左分支表示字符’0’,右分支表示字符’1’,则可以从根结点到叶子结点的路径上分支字符组成的字符串作为该叶子结点字符的编码。如此得到的必为二进制前缀编码。
构造Huffman编码
如何得到使电文总长最短的二进制前缀编码呢?其实使用Huffman树得到的Huffman编码即为总长最短的二进制前缀编码,称为Huffman编码。
证明:假设每种字符在电文中出现的次数为
w
i
w_i
wi,其编码长度为
l
i
l_i
li,电文中有n种字符,则电文总长为
∑
i
=
1
n
w
i
l
i
\\sum_{i=1}^n{w_i}{l_i}
i=1∑nwili
对应到二叉树上,若置
w
i
w_i
wi为叶子结点的权,
l
i
l_i
li恰为从根到叶子结点的路径长度,则
∑
i
=
1
n
w
i
l
i
\\sum_{i=1}^n{w_i}{l_i}
∑i=1nwili恰为二叉树上带权路径长度,而这个带权路径长度最短时的二叉树就是Huffman树,由此得到的二进制前缀编码便称为Huffman编码。
构造Huffman编码的具体步骤:
(其实就是先构造出Huffman树,然后按照左0右1或者左1右0的原则进行编码即可)
给定n个频率(作为权值)分别为
w
1
w_1
w1,
w
2
w_2
w2,… ,
w
n
w_n
wn的字符(作为独立结点).
<1>将这n个结点分别作为n棵只含有一个结点的二叉树,构成森林F。
<2>构造一个新结点,从F中选取两棵根结点权值最小的树作为新结点的左、右子树,并且将新结点的权值置为左、右子树上根结点的权值之和。
<3>从F中删除刚才选出的两棵树,同时将新得到的树加入F中。
<4>重复<2>和<3>步骤,直至F中只剩下一棵树为止。
<5>显然,所有字符结点都出现在叶子结点中。从根结点开始,左分支标’0’,右分支标’1’,那么字符的Huffman编码为从根结点到该叶子结点的路径上边标记的序列。
注:0和1究竟是表示左子树还是表示右子树没有明确规定。因此左、右结点的顺序是任意的。
所以构造出的Huffman树并不唯一,但各个Huffman树的带权路径长度相同且为最优。
用上一节构造的Huffman树写出各个字符的Huffman编码:
以上是关于Huffman树和Huffman编码的主要内容,如果未能解决你的问题,请参考以下文章