我们也可以种植“哈夫曼树”

定义:
  
给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman tree)。

基本术语:
  哈夫曼树(霍夫曼树)又称为最优树.

  1、路径和路径长度 在一棵树中,从一个结点往下可以达到的孩子或子孙结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。

  2、结点的权及带权路径长度 若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。

  3、树的带权路径长度 树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。

构造:

哈夫曼树的构造 假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为:
(1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);
(2) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;
(3)从森林中删除选取的两棵树,并将新树加入森林;
(4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。
我们也可以种植“哈夫曼树”

 

下面给出实现代码:

  1 #include <string.h>
  2 #include <stdlib.h>
  3 #include <stdio.h>
  4 
  5 int m,s1,s2;
  6 
  7 typedef struct
  8 {
  9     int weight;
 10     int parent, lchild, rchild;
 11 
 12 }HTNode, *HuffmanTree;//动态分配数组存储哈夫曼树
 13 
 14 typedef char *HuffmanCode;//动态分配数组存储哈夫曼编码表
 15 
 16 
 17 /**********************************************
 18  *
 19  *    对数组huf[1..n+i-1]中无双亲的结点权值进行
 20  *  排序,s1, s2将是无双亲且权重最小的两个结点下标
 21  *
 22  *********************************************/
 23 void Select(HuffmanTree HT, int n)
 24 {
 25 
 26     int i, j;
 27     for(i = 1; i <= n; i++)
 28     {
 29         if(!HT[i].parent)
 30         {
 31             s1 = i;
 32             break;
 33         }
 34     }
 35 
 36     for(j = i+1; j <= n; j++)
 37     {
 38         if(!HT[j].parent)
 39         {
 40             s2 = j;
 41             break;
 42         }
 43 
 44         for(i = 1; i <= n; i++)
 45         {
 46             if((HT[s1].weight>HT[i].weight)&&(!HT[i].parent)&&(s2!=i))
 47                 s1=i;
 48         }
 49 
 50         for(j = 1; j <= n; j++)
 51         {
 52             if((HT[s2].weight>HT[j].weight)&&(!HT[j].parent)&&(s1!=j))
 53                 s2=j;
 54         }
 55     }
 56 }
 57 
 58 /**********************************************
 59  *
 60  *    构造哈夫曼树的算法
 61  *  哈夫曼树存放在静态链表huf[]中,w存放结点权重
 62  *  这里的 w 全都是 w[i] > 0的
 63  *  n 是叶子个数,最后的编码放在 cd[]
 64  *
 65  *********************************************/
 66 void HuffmanCoding (HuffmanTree HUF, HuffmanCode HC[], int *w, int n)
 67 {
 68 
 69     int i, j, p, cdlen;
 70     char *cd;
 71 
 72     if (n <= 1) return; // 树为空或不存在,退出
 73 
 74     m = 2 * n - 1;
 75 
 76     HUF = (HuffmanTree)malloc((m+1) * sizeof(HTNode)); // 0号单元未用
 77     for (i = 1; i <= n; i++)
 78     {
 79         HUF[i].weight=w[i-1];
 80         HUF[i].parent=0;
 81         HUF[i].lchild=0;
 82         HUF[i].rchild=0;
 83     }
 84     for (i = n+1; i <= m; i++)
 85     {
 86         HUF[i].weight=0;
 87         HUF[i].parent=0;
 88         HUF[i].lchild=0;
 89         HUF[i].rchild=0;
 90     }
 91     printf("\n哈夫曼树的构造过程如下所示:\n");
 92     printf("HUF 初态:\n 结点 weight parent lchild rchild");
 93     for (i=1; i <= m; i++)
 94         printf("\n%4d%8d%8d%8d%8d", i, HUF[i].weight, HUF[i].parent,HUF[i].lchild, HUF[i].rchild);
 95     for (i = n+1; i<=m; i++)
 96     {
 97         //?在HT[1..i-1]中选择parent为0且weight最小的两个结点
 98         //?其序号分别为s1和s2
 99 
100         Select(HUF, i-1);
101         HUF[s1].parent = i;
102         HUF[s2].parent = i;
103         HUF[i].lchild = s1;
104         HUF[i].rchild = s2;
105         HUF[i].weight = HUF[s1].weight + HUF[s2].weight;
106         printf("\nselect: s1=%d s2=%d\n", s1, s2);
107         printf(" 结点 weight parent lchild rchild");
108         for (j = 1; j <= i; j++)
109             printf("\n%4d%8d%8d%8d%8d",j,HUF[j].weight, HUF[j].parent,HUF[j].lchild, HUF[j].rchild);
110     }
111     //------无栈非递归遍历哈夫曼树,求哈夫曼编码
112     cd=(char *)malloc(n*sizeof(char));
113     //分配求编码的工作空间
114     p = m; cdlen = 0;
115     for (i=1; i<=m; ++i) // 遍历哈夫曼树时用作结点状态标志
116         HUF[i].weight = 0;
117     while (p)
118     {
119         if (HUF[p].weight==0)
120         { // 向左
121             HUF[p].weight = 1;
122             if (HUF[p].lchild != 0)
123             {
124                 p = HUF[p].lchild;
125                 cd[cdlen++] = '0';
126             }
127             else if (HUF[p].rchild == 0)
128             { // 登记叶子结点的字符的编码
129                 HC[p] = (char *)malloc((cdlen+1) * sizeof(char));
130                 cd[cdlen] = '\0';
131                 strcpy(HC[p], cd);// 复制编码(串)
132             }
133         }
134         else if (HUF[p].weight==1)
135         {// 向右
136             HUF[p].weight = 2;
137             if (HUF[p].rchild != 0)
138             {
139                 p = HUF[p].rchild;
140                 cd[cdlen++] = '1';
141             }
142         }
143         else
144         { // HT[p].weight==2,退回退到父结点,编码长度减1
145             HUF[p].weight = 0;
146             p = HUF[p].parent;
147             --cdlen;
148         }
149     }
150 
151 }
152 
153 
154 /**********************************************
155  *
156  *    程序入口
157  *
158  *********************************************/
159 int main()
160 {
161 
162     HuffmanTree huf = (HuffmanTree)malloc(sizeof(HTNode));
163     HuffmanCode *hc;
164     int *w, n, i;
165     printf("输入结点数: ");
166     scanf("%d",&n);
167     hc = (HuffmanCode *)malloc(n * sizeof(HuffmanCode));
168     w = (int *)malloc(n*sizeof(int));
169     printf("输入%d个结点的权值:\n", n);
170     for (i = 0; i < n; i++)
171     {
172         printf("w[%02d]: ", i+1);
173         scanf("%d",&w[i]);
174     }
175     HuffmanCoding(huf, hc, w, n);
176     printf("\n各结点的哈夫曼编码:\n");
177     for(i = 1; i <= n; i++)
178         printf("%2d(%4d):%s\n", i, w[i-1], hc[i]);
179     getchar();
180 
181     return 0;
182 }

 

 

你可能感兴趣的:(哈夫曼树)