5.2 哈夫曼树和哈夫曼编码
查找,或者使用数据不是平均的使用每一个数据,而是有的数据频率高,有的数据频率低,不同的频率可以视为数据不同的权重,这些带权重的数据在树中的分布会影响到查找的效率。
树中的每个叶节点都有自己的权重,从根节点到叶节点经过的路径乘以叶节点的权重作为某个叶节点的带权路径长度,所有的叶节点的带权路径长度之和就是该树的带权路径,哈夫曼树就是带权路径最小的树。
构造哈夫曼树:在数据中找到权值最小的的两个,组成一个树,这棵树的权值就是他们两个权值的和,然后这棵新树的根节点作为新的节点放入原来的数据中。
typedef struct TreeNode *HuffmanTree;
struct TreeNode{
int weight;
HuffmanTree Right,Left;
};
//事先将元素按权值构成最小堆。传入最小堆,返回哈夫曼树。
HuffmanTree Huffman(MinHeap H){
HuffmanTree T;
for(int i = 1; i < H->Size; i++){
T = malloc(sizeof(struct TreeNode));
T->Left = Delete(H);
T->Right = Delete(H);
T->weight = T->Left->weight+T->Right->weight;
Insert(T,H);
}
T = Delete(H);
return T;
}
哈夫曼树有几个特点:
- 没有只有一个儿子的节点(也就是度为1的节点)
- 如果有n个叶节点,总结点数为2n-1
- 如果有权值一样的元素,有可能会构成不同结构的树,但是他们的总路径是相同的
- 哈夫曼树左右儿子交换位置之后任然为哈夫曼树。
哈夫曼编码:对于使用频率高的字符,我们需要用更简洁的表示方法,但是用不等长的编码方法就会出现二义性问题,同样的二进制码可以表示不同的意义,为了避免这种情况,我们就要用到哈夫曼树,每个节点的分支都有0和1来表示,要编码的字符都在树的叶节点上,这样就可以避免二义性问题,再把不同频率作为权值,就可以用哈夫曼树做哈夫曼编码。(这让我想到了有的CPU指令集)