哈夫曼树的基本概念
例子:将学生的百分制成绩转换为五分制成绩
<60:E 60-69:D 70-79:C 80-89:B 90-100:A
判断树:用于描述分类过程的二叉树
若每次的输入量很大,则应考虑程序的操作时间.
能不能找到一种效率最高的判断树呢?
哈夫曼树(最优二叉树)
- 路径:从树中一个结点到另一个结点之间的分支构成这两个结点间的路径
- 结点的路径长度:两结点间路径上的分支数
- 输的路径长度:从根结点到每一个结点的路径长度之和.记作TL
- 结点数目相同的二叉树中,完全二叉树是路径长度最短的二叉树
- 权(weight):将树中结点赋予一个有某种含义的数值,则这个数值称为该结点的权
- 结点的戴荃路径长度:从根结点到该结点之间的路径长度与该结点的权的乘积
- 树的带权路径长度:树中所有叶子结点的带权路径长度之和.记作WPL
哈夫曼树
最优树,带权路径长度最短的树-----带权路径长度最短,是在度相同的树中比较而得出的结果,因此有最优二叉树,最优三叉树等等.
哈夫曼树的构造算法
哈夫曼树中权越大的叶子离根越近
贪心算法:构造哈夫曼树时首先选择权值小的叶子结点
-
根据n个给定的权值{w1,w2,…,wn}构成n棵二叉树的森林F={T1,T2,…,Tn},其中Ti只有一个带权为wi的根结点.(构造森林全是根)
-
在F中选取两棵树根结点的权值最小的树作为左右子树,构造一棵新的二叉树,且设置新的二叉树的根结点的权值为其左右子树上根节点的权值之和.(选用两小造新树)
-
在F中删除这两棵树,同时将新得到的二叉树加入到森林中.(删除两小添新人)
-
重复2和3步骤,直到森林中只有一棵树为止,这棵树即为哈夫曼树.
结论
哈夫曼树的结点的度数为0或2,没有度为1的结点
包含n棵树的森林要经过n-1次合并才能形成哈夫曼树,共产生n-1个新结点.
包含n个叶子结点的哈夫曼树中共有2n-1个结点.
哈夫曼树构造算法的实现
- 采用顺序存储结构-----一维结构数组HuffmanTree H;
//哈夫曼树---------------------------------------
typedef struct {
int weight;
int parent, lch, rch;
}HTNode, *HuffmanTree;
1. 初始化HT[1…2n-1]:lch=rch=parent=0;
2. 输入初始n个叶子结点:置HT[1…n]的weight值;
3. 进行以下n-1次合并,依次产生n-1个结点HT[i],i=n+1…2n-1
3.1 . 在HT[1…i-1]中选两个未被选过(从parent==0的结点中选)的weight最小的两个结点HT[s1]和HT[s2],(s1和s2是两个最小结点的下标)
3.2 . 修改HT[s1]和HT[s2]的parent值:HT[s1].parent=i;HT[s2].parent=i
3,3 . 修改新产生的HT[i]: HT[i].weight=HT[s1].weight+HT[s2].weight; HT[i].lch = s1;HT[s2].rch = s2;
哈夫曼编码及方法
在远程通信中,要将待传字符转换成由二进制的字符串,若将编码设计为长度不等的二进制编码,即让待传字符中出现次数较多的字符采用尽可能短的编码,则转换的二进制字符串便可能减少
关键:要设计长度不等的编码,则必须使任一字符的编码都不是另一个字符的编码的前缀,无重码.
- 统计字符集中每个字符在电文中出现的平均概率(概率越大,要求编码越短).
- 利用哈夫曼树的特点:权越大的叶子离根越近;将每个字符的概率值作为权值,构造哈夫曼树.则概率越大的结点,路径越短.
- 在哈夫曼树的每个分支上标0或1:结点的左分支标0,右分支标1,吧从根结点到每个叶子的路径上的标号连接起来,作为该叶子代表的字符的编码.
两个问题
- 为什么哈夫曼编码能够保证是前缀编码?
因为没有一片树叶是另一片树叶的祖先,所以每个叶子结点的编码就不可能是其他叶子结点编码的前缀
- 为什么哈夫曼编码能够保证字符编码总长最短?
因为哈夫曼树的带权路径长度最短,故字符编码的总长最短
文件的编码和解码
编码
- 输入个字符及其权值
- 构造哈夫曼树-----HT[i]
- 进行哈夫曼编码-----HC[i]
- 查HC[i],得到各字符的哈夫曼编码
解码
- 构造哈夫曼树
- 依次读入二进制码
- 读入0,则走向左孩子;读入1,则走向右孩子
- 一旦到达某叶子时,即可译出字符
- 然后再从根出发继续译码,直到结束