赫夫曼树 —— 严蔚敏版教程配套算法代码
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 }