1 // 赫夫曼树 —— 严蔚敏版教程配套算法代码
2
3 #include<stdio.h>
4 #include<stdlib.h>
5 #include<string.h>
6
7 //哈夫曼树结点结构
8 typedef struct {
9 int weight;//结点权重
10 int parent, left, right;//父结点、左孩子、右孩子在数组中的位置下标
11 }HTNode, * HuffmanTree;
12
13 //动态二维数组,存储哈夫曼编码
14 typedef char** HuffmanCode;
15
16 //HT数组中存放的哈夫曼树,end表示HT数组中存放结点的最终位置,s1和s2传递的是HT数组中权重值最小的两个结点在数组中的位置
17 void Select(HuffmanTree HT, int end, int* s1, int* s2)
18 {
19 int min1, min2;
20
21 int i = 1; //遍历数组初始下标为 1
22
23 while (HT[i].parent != 0 && i <= end) { //找到还没构建树的结点
24 i++;
25 }
26 min1 = HT[i].weight;
27 *s1 = i;
28 i++;
29 while (HT[i].parent != 0 && i <= end) {
30 i++;
31 }
32 //对找到的两个结点比较大小,min2为大的,min1为小的
33 if (HT[i].weight < min1) {
34 min2 = min1;
35 *s2 = *s1;
36 min1 = HT[i].weight;
37 *s1 = i;
38 }else {
39 min2 = HT[i].weight;
40 *s2 = i;
41 }
42 //两个结点和后续的所有未构建成树的结点做比较
43 for (int j = i + 1; j <= end; j++)
44 {
45 //如果有父结点,直接跳过,进行下一个
46 if (HT[j].parent != 0) {
47 continue;
48 }
49 //如果比最小的还小,将min2=min1,min1赋值新的结点的下标
50 if (HT[j].weight < min1) {
51 min2 = min1;
52 min1 = HT[j].weight;
53 *s2 = *s1;
54 *s1 = j;
55 }
56 //如果介于两者之间,min2赋值为新的结点的位置下标
57 else if (HT[j].weight >= min1 && HT[j].weight < min2) {
58 min2 = HT[j].weight;
59 *s2 = j;
60 }
61 }
62 }
63
64 //HT为地址传递的存储哈夫曼树的数组,w为存储结点权重值的数组,n为结点个数
65 void CreateHuffmanTree(HuffmanTree* HT, int* w, int n)
66 {
67 if (n <= 1) return; // 如果只有一个编码就相当于0
68 int m = 2 * n - 1; // 哈夫曼树总节点数,n就是叶子结点
69 *HT = (HuffmanTree)malloc((m + 1) * sizeof(HTNode)); // 0号位置不用
70 HuffmanTree p = *HT;
71
72 // 初始化哈夫曼树中的所有结点
73 for (int i = 1; i <= n; i++)
74 {
75 (p + i)->weight = *(w + i - 1);
76 (p + i)->parent = 0;
77 (p + i)->left = 0;
78 (p + i)->right = 0;
79 }
80
81 //从树组的下标 n+1 开始初始化哈夫曼树中除叶子结点外的结点
82 for (int i = n + 1; i <= m; i++)
83 {
84 (p + i)->weight = 0;
85 (p + i)->parent = 0;
86 (p + i)->left = 0;
87 (p + i)->right = 0;
88 }
89
90 //构建哈夫曼树
91 for (int i = n + 1; i <= m; i++)
92 {
93 int s1, s2;
94 Select(*HT, i - 1, &s1, &s2);
95 (*HT)[s1].parent = (*HT)[s2].parent = i;
96 (*HT)[i].left = s1;
97 (*HT)[i].right = s2;
98 (*HT)[i].weight = (*HT)[s1].weight + (*HT)[s2].weight;
99 }
100 }
101
102 //HT为哈夫曼树,HC为存储结点哈夫曼编码的二维动态数组,n为结点的个数
103 void HuffmanCoding(HuffmanTree HT, HuffmanCode* HC, int n) {
104 *HC = (HuffmanCode)malloc((n + 1) * sizeof(char*));
105 char* cd = (char*)malloc(n * sizeof(char)); //存放结点哈夫曼编码的字符串数组
106 cd[n - 1] = '\0';//字符串结束符
107
108 for (int i = 1; i <= n; i++) {
109 //从叶子结点出发,得到的哈夫曼编码是逆序的,需要在字符串数组中逆序存放
110 int start = n - 1;
111 //当前结点在数组中的位置
112 int c = i;
113 //当前结点的父结点在数组中的位置
114 int j = HT[i].parent;
115 // 一直寻找到根结点
116 while (j != 0) {
117 // 如果该结点是父结点的左孩子则对应路径编码为0,否则为右孩子编码为1
118 if (HT[j].left == c)
119 cd[--start] = '0';
120 else
121 cd[--start] = '1';
122 //以父结点为孩子结点,继续朝树根的方向遍历
123 c = j;
124 j = HT[j].parent;
125 }
126 //跳出循环后,cd数组中从下标 start 开始,存放的就是该结点的哈夫曼编码
127 (*HC)[i] = (char*)malloc((n - start) * sizeof(char));
128 strcpy((*HC)[i], &cd[start]);
129 }
130 //使用malloc申请的cd动态数组需要手动释放
131 free(cd);
132 }
133 //打印哈夫曼编码的函数
134 void PrintHuffmanCode(HuffmanCode htable, int* w, int n)
135 {
136 printf("Huffman code : \n");
137 for (int i = 1; i <= n; i++)
138 printf("%d code = %s\n", w[i - 1], htable[i]);
139 }
140 int main(void)
141 {
142 int w[5] = { 2, 8, 7, 6, 5 };
143 int n = 5;
144 HuffmanTree htree;
145 HuffmanCode htable;
146 CreateHuffmanTree(&htree, w, n);
147 HuffmanCoding(htree, &htable, n);
148 PrintHuffmanCode(htable, w, n);
149 system("pause");
150 return 0;
151 }