哈夫曼树的基本概念
哈夫曼(Huffman)树又称最优树,是一类带权路径长度最短的树,在实际中有广泛的用途。哈夫曼树的定义,涉及路径、路径长度、权等概念,下面先给出这些概念的定义,然后再介绍哈夫曼树。
(1)路径:从树中一个结点到另一个结点之间的分支构成这两个结点之间的路径。
(2)路径长度:路径上的分支数目称作路径长度。
(3)树的路径长度:从树根到每一结点的路径长度之和。 (4) 权:赋予某个实体的一个量,是对实体的某个或某些属性的数值化描述。在数据结构中,实体有结点(元素)和边(关系)两大类,所以对应有结点权和边权。结点权或边权具体代表什么意义,由具体情况决定。如果在一棵树中的结点上带有权值,则对应的就有带权树等概念。
(5)结点的带权路径长度:从该结点到树根之间的路径长度与结点上权的乘积。
(6)树的带权路径长度:树中所有叶子结点的带权路径长度之和,通常记作[插图]。
(7)哈夫曼树:假设有m个权值{w1, w2,…, wm},可以构造一棵含n个叶子结点的二叉树,每个叶子结点的权为wi,则其中带权路径长度WPL最小的二叉树称做最优二叉树或哈夫曼树。
哈夫曼树的构造算法
1.哈夫曼树的构造过程(
1)根据给定的n个权值{w1, w2,…, wn},构造n棵只有根结点的二叉树,这n棵二叉树构成一个森林F。
(2)在森林F中选取两棵根结点的权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为其左、右子树上根结点的权值之和。
(3)在森林F中删除这两棵树,同时将新得到的二叉树加入F中。
(4)重复(2)和(3),直到F只含一棵树为止。这棵树便是哈夫曼树。
在构造哈夫曼树时,首先选择权小的,这样保证权大的离根较近,这样一来,在计算树的带权路径长度时,自然会得到最小带权路径长度,这种生成算法是一种典型的贪心法。
构造哈夫曼树以及代码实现
【算法步骤】构造哈夫曼树算法的实现可以分成两大部分。
① 初始化:首先动态申请2n个单元;然后循环2n-1次,从1号单元开始,依次将1至2n-1所有单元中的双亲、左孩子、右孩子的下标都初始化为0;最后再循环n次,输入前n个单元中叶子结点的权值。
② 创建树:循环n-1次,通过n−1次的选择、删除与合并来创建哈夫曼树。选择是从当前森林中选择双亲为0且权值最小的两个树根结点s1和s2;删除是指将结点s1和s2的双亲改为非0;合并就是将s1和s2的权值和作为一个新结点的权值依次存入到数组的第n+1之后的单元中,同时记录这个新结点左孩子的下标为s1,右孩子的下标为s2。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//-----Huffman树的存储结构
typedef struct
{
unsigned int weight;
unsigned int parent,lchild,rchild;
}HTNode,*HuffmanTree;
typedef char** HuffmanCode; //存储哈夫曼编码
void Select(HuffmanTree Ht,int n,int* s1,int* s2);
void HuffmanCoding(HuffmanTree* HT,HuffmanCode* HC,int* w,int n){
HuffmanTree Ht = *HT;
HuffmanTree p,p1;
int i;
if(n <= 1) return;
int m = 2*n-1;
Ht = (HuffmanTree)malloc((m+1)*sizeof(HTNode));//0号空间不用
int* w1 = w;
for(p = Ht ,i = 1;i<=n;++i) {//为1到n号空间赋权值
p++;
w++;
p->weight = *w;
p->parent = 0;
p->lchild = 0;
p->rchild = 0;
}
for(;i<=m;++i) { //为剩下的空间赋值
p++;
p->weight = 0;
p->parent = 0;
p->lchild = 0;
p->rchild = 0;
}
printf("\t\t初始数据\n序号 weight parent lchild rchild\n");
for(p1 = Ht,i=1;i<=m;++i){
printf("%d\t%d\t%d\t%d\t%d\t\n",i,p1[i].weight,p1[i].parent,p1[i].lchild,p1[i].rchild);
}
int s1,s2;
for(i = n+1;i<=m;i++){
Select(Ht,i-1,&s1,&s2);
Ht[s1].parent = i;
Ht[s2].parent = i;
Ht[i].lchild = s1;
Ht[i].rchild = s2;
Ht[i].weight = Ht[s1].weight + Ht[s2].weight;
}
printf("---------------HuffmanTree----------\n");
printf("序号 weight parent lchild rchild\n");
for(p1 = Ht,i=1;i<=m;++i){
printf("%d\t%d\t%d\t%d\t%d\t\n",i,p1[i].weight,p1[i].parent,p1[i].lchild,p1[i].rchild);
}
HuffmanCode Hc = *HC ;
HuffmanCode p2;
Hc = (HuffmanCode)malloc((n+1)*sizeof(char*));
char* cd;
cd = (char*)malloc(n*sizeof(char));
cd[n-1] = '\0';
for(i = 1;i<=n;++i){
int start = n-1;
int c,f;
for(c=i,f = Ht[i].parent;f!=0;c=f,f=Ht[f].parent){
if(Ht[f].lchild == c) cd[--start] = '0';
else cd[--start] = '1';
}
Hc[i] = (char*)malloc((n-start)*sizeof(char));
strcpy(Hc[i],&cd[start]);
}
free(cd);
printf("\n");
char s[8] = {'a','b','c','d','e','f','g','h'};
for(p2 = Hc,i=1;i<=n;++i){
printf("%c的权值为%d哈夫曼编码:",s[i-1],w1[i]);
printf("%4s\n",p2[i]);
}
}
void Select(HuffmanTree Ht,int n,int* s1,int* s2){
int i ;
*s1 = 0;
*s2 = 0;
for(i=1;i<=n;i++){
if(Ht[i].parent==0){
if(Ht[i].weight<=Ht[*s1].weight){
*s2 = *s1;
*s1 = i;
}else if(Ht[i].weight<Ht[*s2].weight)
*s2 = i;
}
}
}
int main() {
int w[9] = {999,5,29,7,8,14,23,3,11};
HuffmanTree HT;
HuffmanCode HC;
HuffmanCoding(&HT,&HC,w,8);
return 0;
}
运行结果
初始数据
序号 weight parent lchild rchild
1 5 0 0 0
2 29 0 0 0
3 7 0 0 0
4 8 0 0 0
5 14 0 0 0
6 23 0 0 0
7 3 0 0 0
8 11 0 0 0
9 0 0 0 0
10 0 0 0 0
11 0 0 0 0
12 0 0 0 0
13 0 0 0 0
14 0 0 0 0
15 0 0 0 0
---------------HuffmanTree----------
序号 weight parent lchild rchild
1 5 9 0 0
2 29 14 0 0
3 7 10 0 0
4 8 10 0 0
5 14 12 0 0
6 23 13 0 0
7 3 9 0 0
8 11 11 0 0
9 8 11 7 1
10 15 12 3 4
11 19 13 9 8
12 29 14 5 10
13 42 15 11 6
14 58 15 12 2
15 100 0 13 14
a的权值为5哈夫曼编码:0001
b的权值为29哈夫曼编码: 11
c的权值为7哈夫曼编码:1010
d的权值为8哈夫曼编码:1011
e的权值为14哈夫曼编码: 100
f的权值为23哈夫曼编码: 01
g的权值为3哈夫曼编码:0000
h的权值为11哈夫曼编码: 001
--------------------------------
Process exited after 0.4508 seconds with return value 0
请按任意键继续. . .