定义:
给定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 }