[数据结构]哈弗曼树
哈夫曼树应用实例——哈夫曼编码
关键:要设计长度不等的编码,则必须使任一字节的编码都不是另一字符的编码的前缀–前缀编码。
译码过程:
分解接收字符串,遇“0”向左,遇“1”向右,一旦达到叶子结点,则译出一个字符,反复由根出发,直到译码完成。
特点:每一码都不是另一码的前缀,绝不会译错。
哈弗曼树的构造
哈弗曼树:带权路径长度最小的树。
相关术语:
路径:由一结点到另一结点间的分支所构成
路径长度:路径上的分支数目
带权路径长度:结点到根的路径长度与结点上权的乘积
树的带权路径长度:树中所有叶子结点的带权路径长度之和
哈弗曼树的构造过程
基本思想:使权大的结点靠近根。
操作要点:对权值的合并、删除与替换,总是合并当前值最小的两个。
哈夫曼编码的构造
基本思想:概率大的字符用短码,小的用长码,构造哈弗曼树。
构造过程:
①根据给定的n个权值{W1,W2,W3,……Wn},构造n棵只有根结点的二叉树。
②在森林中选取两棵根结点权值最小的树作左右子树,构造一棵新的二叉树,置新二叉树根结点权值为其左右子树根结点权值之和。
③在森林中删除这两棵树,同时将新得到的二叉树加入森林中。
④重复上述两步,直到只含一棵树为止,这棵树即哈弗曼树。
哈弗曼树构造算法的实现
typedef struct
{
int weght;
int parent,lch,rch;
}*HuffmanTree;
Huffman树的建立
//算法5.8 建立Huffman树
void CreateHuffmanTree(HuffmanTree &HT,int n){
HT = new HTNode[2*n];
for (int i = 1; i <= n; i++)
cin >> HT[i].weight;
for (int i = 0; i < n * 2; i++)
HT[i].parent = HT[i].lchild = HT[i].rchild = 0;
for(int i = n+1 ; i < 2*n ; i ++){
int res1;
int res2;
Select(HT,i-1,res1,res2);
HT[i].weight = HT[res1].weight + HT[res2].weight;
HT[i].lchild = res1;
HT[i].rchild = res2;
HT[res1].parent = i;
HT[res2].parent = i;
}
}
Huffman编码
//算法5.9 构造并输出Huffman编码
void CreateHuffmanCode(HuffmanTree &HT,HuffmanCode &HC,int n)
{
HC = new char*[n+1];
char *cd = new char[n];
cd[n-1] = '\0';
int start,c,f;
for(int i=1;i<=n;i++)
{
start = n-1;
c = i;
f = HT[i].parent;
while(f)
{
--start;
if(HT[f].lchild == c ) cd[start] = '0';
else cd[start] = '1';
c = f ;
f = HT[f].parent;
}
HC[i] = new char[n-start];
strcpy(HC[i],&cd[start]);
}
for(int i=1;i<=n;i++)
{
cout << HC[i]<<endl;
}
}
哈夫曼编码的几点结论
● 哈夫曼编码是不等长编码。
● 哈夫曼编码是前缀编码,即任一字符的编码都不是另一字符编码的前缀。
● 哈夫曼编码树中没有度为1的结点。若叶子结点的个数为n,则哈夫曼编码树的结点总数为2n-1。
● 发送过程:根据由哈夫曼树得到的编码表送出字符数据。
● 接收过程:按左0、右1的规定,从根结点走到一个叶结点,完成一个字符的译码。反复此过程,直到接收数据结束。
应用
● 利用二叉树求解表达式的值