哈夫曼树及其编码
首先是哈夫曼树的定义:在一棵二叉树中,带权路径长度达到最小,成这样的树是最优二叉树,也是哈弗曼树。大概意思就是把数值大的节点放在树上面,数值小的节点放在树下面。哈夫曼树的结构使用顺序结构,这里直接使用了数组。
建造哈弗曼树的思路:根据二叉树的性质,有n个叶子节点,二叉树就会有2n-1个节点。定义一个数组,前n个节点作为叶子节点,从前n个节点中选择两个最小的节点,作为一棵二叉树的左右节点,这棵二叉树的根节点值为左右孩子节点值之和。把这个根节点的值放入数组中,然后接着从数组中(新加入的节点也算)选取两个最小的节点,只不过把已经使用过的节点忽略掉,这样不断循环,每个节点和其他节点都有对应关系。
代码实现:
定义结构体,里面有四个成员,分别为权值,父节点,左孩子节点,右孩子节点,这里的节点指的是他在数组中对应的下标位置,如果是0则没有父节点或者孩子节点,权值不能为0.
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #define n 7 5 #define m 2*n-1 6 #define maxval 100 7 8 typedef struct{ 9 int weight; 10 int parent,lchild,rchild; 11 }HufmTree; 12 13 HufmTree *init(){ 14 15 HufmTree *hf = (HufmTree *)malloc(sizeof(HufmTree)); 16 if(hf==NULL) 17 return NULL; 18 19 return hf; 20 21 } 22 23 void setHuffmanTree(HufmTree *tree[]){ 24 25 int i,j,p1,p2,s1,s2; 26 for(i=n;i<m;i++){ //从n位置开始,都是两个节点新合成的节点,直到m位置结束 27 p1 = p2 = 0; //每次循环开始,都要数组从头开始遍历 28 s1 = s2 = maxval; 29 for(j=0;j<=i-1;j++) //在所有节点中寻找,i后面没有数值的空位置用不到 30 if(tree[j]->parent==0) //此处的代码的作用是检查该节点是否已经使用过,因为使用过的节点肯定有父节点 31 if(tree[j]->weight<s1){ //寻找最小权值的节点 32 s2 = s1; 33 s1 = tree[j]->weight; 34 p2 = p1; 35 p1 = j; 36 } 37 else if(tree[j]->weight<s2){//寻找次小权值的节点 38 s2 = tree[j]->weight; 39 p2 = j; 40 } 41 tree[p1]->parent = tree[p2]->parent = i; //把两个最小节点的父节点指向i 42 tree[i]->weight = tree[p1]->weight + tree[p2]->weight; //父节点权值等于左右孩子权值之和 43 tree[i]->lchild = p1; //指明父节点的左右孩子节点 44 tree[i]->rchild = p2; 45 } 46 47 } 48 49 void display(HufmTree *tree[n]){ 50 51 int i; 52 for(i=0;i<m;i++){ 53 printf("%d ",tree[i]->weight); 54 } 55 printf("\n"); 56 }
哈弗曼编码:使出现频率高的编码放在上面,频率低的编码放在下面。
以上图为例,其中A的频率最高,离根节点最近,GFED频率最低,在最下面。
实现原理:从根节点到每个叶子节点间的路径值作为编码,图片里的规定是左子树的路径为1,右子树的路径为0,这样每个节点都有自己的编码且不会重复
1 void createHuffmanCode(HufmTree *tree[n]){ 2 3 4 int i,j,p,start; 5 char *temp; //临时存放字母编码的数组 6 for(i=0;i<n;i++){ //从0~n之间都是二叉树的叶子节点 7 start = n-1; //存放temp数组的下标,从后往前,指向最后面 8 temp = (char *)malloc(n*sizeof(char)); //为数组分配空间 9 temp[start] = '\0'; //添加字符串结束标志 10 j = i; //j代表为哪个叶子节点编码 11 p = tree[j]->parent; //p是j的父节点 12 while(p!=0){ 13 if(tree[p]->lchild==j) 14 temp[--start] = '0'; //如果j是p的左孩子,则在数组中添加一个 '0' 15 else 16 temp[--start] = '1'; //反之则添加一个 '1' 17 j = p; 18 p = tree[p]->parent; //j和p不断往上循环 19 } 20 printf("%d:%s \n",tree[i]->weight,&temp[start]); 21 } 22 23 } 24 25 void main(){ 26 27 HufmTree *tree[m]; 28 int i,num[n] = {7,3,2,4,9,10,5}; 29 30 //分配空间 31 for(i=0;i<m;i++) 32 tree[i] = init(); 33 34 //数组每个节点内的所有值初始化时都设为0 35 for(i=0;i<m;i++){ 36 tree[i]->weight = 0; 37 tree[i]->parent = 0; 38 tree[i]->rchild = 0; 39 tree[i]->lchild = 0; 40 } 41 42 //为数组前n个数的权值赋值 43 for(i=0;i<n;i++) 44 tree[i]->weight = num[i]; 45 46 setHuffmanTree(tree); 47 48 display(tree); 49 50 createHuffmanCode(tree); 51 52 }