开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第19天,点击查看活动详情
树之习题选讲-Huffman Codes(哈夫曼)
树习题-HC.1 题意理解
Huffman编码不唯一
注意:最优编码不一定通过Huffman算法得到,但是Huffman算法一定能得到最优编码
Huffman Codes 的特点
- 最优编码——总长度(WPL)最小
- 无歧义解码——前缀码:数据仅存于叶子结点
前缀码对应到一颗二叉树里面意味着每一个字符都要放在这棵树的叶子结点上
如果有任何一个字符放在了中间的内部结点上,那就意味着一定存在另外一个字符(他的编码会经过这个字符的编码),也就是说这个字符就会成为另外一个字符的前缀编码,这样解码的时候就会有歧义
- Huffman树是没有度为1的结点——满足1、2则必然有3
什么是度为1的结点:
“度是一个计算机的单位,度为1的节点就说明该处的子节点个数为1,度为2就说明个数为2,而度为0的结点叫叶子结点,由二叉树的性质可以知道,二叉树中叶子结点总是比度为2的结点多一个。”
这个在程序中不需要判断的,因为满足第一跟第二的话,一定没有度为1的结点
为什么?
反证法:如果在这棵树里面存在一个度为1的结点,那么这个结点,假设他又这样一颗子树的话,他肯定不是叶子结点。那么在这个结点上就一定不会放真正的字符,那么他的这个一棵子树下面,一定是有叶子结点的。那些叶子结点对应着真正的字符的。如果我们把这个结点给他去掉的话,并不影响其他的编码仍然保持为前缀码这个特点,因为所有的字符还都在叶子结点上。
那么他原来子树上所有的叶子结点,所有的编码都会缩短一个点位,于是我们就得到一套更短的编码。但我们说他本身已经是最优的编码了,那已经本身是属于不在可以优化的了,已经没办法得到更短的编码了,所有反证得出一定没有度为1的结点
注意:满足2、3可不一定有1!
上图中两边都符合2、3,但是右边才是最优编码。由此得出符合2、3的不一定是最优编码
树习题-HC.2 计算最优编码长度
-
计算最优编码长度
-
//声明H MinHeap H = CreateHeap(N);//创建一个空的、容量为N的最小堆(调用CreateHeap创建一个容量为N的最 小堆) H = ReadData(N);//将f[]读入H->Data[]中,为后面建立最小堆做好准备 HuffmanTree T = Huffman(H);//建立Huffman树,提示:在调用哈夫曼算法的时候必须要先有一个最小堆(建立最小堆是在哈夫曼算法里面完成的) //哈夫曼算法里面先建立一个最小堆,每次从里面弹出两个最小的结点,然后生成一颗新的树再把它压回去 计算最优编码长度还是需要我们自己去写一个函数的 int CodeLen = WPL(T,0);//CodeLen返回的就应该是这颗哈夫曼树所对应的最优编码总长度 //T是传入的树根,0是开始的地方 //深度是传进去的每一棵子树的根结点他当前的深度是多少,此代码是从整棵树的根结点开始的(当前是0,从0开始) //进行的是一个递归的先序遍历过程 int WPL(HuffmanTree T,int Depth) { if(!T->Left && !T->Right ) return (Depth*T0->Weight);//当前深度乘以他的权重 else//否则T一定有2个孩子 return (WPL(T->Left,Depth+1) + WPL(T->Right,Depth+1));//递归的解决左边的问题和右边的问题,返回两边的和,递归返回的深度是要加1的 }
-
树习题-HC.3 检查编码
2与3冲突、100与1001,如果100的最后一个0是叶子结点,那他就不应该还能继续延伸下去
1与4冲突、1011与101,1011最后一个1是叶子结点,101的最后一个1在内部结点上(因为"1011"相对于"101后面还有一个1,就是说后面还有一个右子树)就冲突了