结点的度:二叉树结点的分支数目, 也就是孩子结点的个数。比如,度为1,表示有一个节点; 度为2,表示有两个结点;度为2,表示没有结点。叶子结点度为0,因为没有孩子结点。
各种结点个数的关系:假设N0 = 叶子结点,度为0的结点总数。N1 =度为1的结点总数。N2 = 度为2的结点总数。
N=所有结点总数之和。
那么有以下公式:
所有树:N=N0+N1+N2
满二叉树:N1=0, N= 2N0 - 1。 哈夫曼树至少是满二叉树, 或者完全二叉树。
权值:每一个叶子结点,都设置一个值,这个值就是权值。
哈夫曼树也叫最优二叉树。给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)
什么是带权路径长度:
看上图, 所有叶子结点的权值 × 深度,之和是带权路径长度。带权路径长度最小的树就是哈夫曼树。
如何构建哈夫曼树:
假设有n个权值结点,以及根据这些结点生成n棵只有一个结点的树, 也就是树集合F:{T1, T2,.....Tn},
1. 从集合F中取出两个最小权值的树.
2. 取出的两个结点作为左右结点, 小的在左边,大的在右边。 跟结点是权值和的结点, 由此组成一个树.
3. 把生成的树,再次返回集合F. 这颗新树的权值是左右结点权值的和.
4. 重复1,2,3步骤, 直到集合F只剩下一颗树. 这颗树就是哈夫曼树.
实践如下:
假如有权值列表:[2, 5, 1, 4, 3]
1. 先从列表里取出两个最小值1和2,组成以3为跟结点的一颗树。[ 5, 4, 3,3]
2. 取出这时取值列表里两个最小值3和3,此时权值列表:[ 5, 4, 6]
3. 取出这时取值列表里两个最小值4和5,此时权值列表:[ 6, 9]
4. 取出这时取值列表里两个最小值6和9,此时权值列表:[ 15]
5. 此时权值列表只有一颗树, 结束。
带权路径长度是:1 × 3 + 2×3+3×2+4×2+5×2 = 27
什么是同权不同构:
同样一组权值列表, 有时, 可以构建出不同形状的树, 但是带权路径长度还是一样。 比如构建中间出现两个相同权值的结点, 新加结点可以放左边也可以放右边,造成了两种不同形状的树。
这种情况都是允许的,选哪棵树都可以。
编码是为了给权值列表中的权值数值分别用0,1编码。在哈夫曼树上,左分支边是0, 右分支边是1。 由此构成编码。如下图,依旧采用上面举例的哈夫曼树:
编码后,1: 000,2:001, 3:01, 4:10, 5:11
这样有什么用处呢? 请看这一穿字符:[ABCABDBBDBEDEED]
A出现2次,B出现5次,C出现1次,D出现4次,E出现3次。这也正好是上面举例的权值列表。
那编了这些码有什么用呢? 用来压缩。 是哈夫曼树的作用。
没有压缩前一个字符用定长的3位表示, 总共需要45位。压缩后是不定长的,而且你会发现出现次数越多的编码长度越短。比如B出现5次,编码是11, 而C只出现一次编码是000。
压缩后是:001 11 000 001 11 10 11 11 10 11 01 10 01 01 10
总共是33位。压缩了12位。
什么是前缀遍码:
哈夫曼编码就是前缀编码。 就是说,不能让一个字母的二进制代表数,为另一个字母的二进制代表数的子串。就是说,不会出现01代表一个字母, 011又代表一个字母。
哈夫曼编码是如果做到的呢?
简单的说,因为都是叶子结点。所以任何一个权值结点,不可能出现在某一个权值的路径上。
哈夫曼是从叶子节点开始构造,构造到根节点的,而且构造时,都是计算两个权值的节点的和再与其他叶子节点再生成一个父节点来组成一个新的树。并且不允许任何带有权值的节点作为他们的父节点。这也保证了所有带有权值的节点都被构造为了叶子节点。然后最后编码的时候是从根节点开始走到叶子节点而得出的编码。在有权值的节点又不会出现在任何一条路的路途中的情况,只会出现在终点的情况下,因此不会出现01代表一个字母, 011又代表一个字母。
使用哈夫曼编码是无损压缩。
哈夫曼编码不是图像压缩的所有,是其中一个环节。不然压缩比才大约有1:0.7。