哈夫曼編碼演算法
阿新 • • 發佈:2020-09-08
輸入:字元及其權值,待譯碼字串,待解碼字串
功能要求:輸出各字元的哈夫曼編碼,輸出譯碼字串,輸出解碼字串
哈夫曼樹構造過程:
1.從所有待編碼的節點中選取權值最小的兩個節點S1,S2,合併成一顆二叉樹T1,T1的葉子節點為S1,S2,根節點為S1,S2之和。
2.選取除這兩個節點以外最小的節點,將其和T1作為葉子構造一棵新的樹。
3.重複上述過程,直到所有代編碼的節點都加入到樹中。
解碼過程:
按照構造的哈夫曼樹,從根節點開始,遇到0往左子樹走,遇到1走向右子樹,這樣就可以達到解碼的過程
程式碼:
1 #include<stdio.h> 2 #include<stdlib.h> 3View Code#include<string.h> 4 #include<conio.h> 5 #define MAXNUM 60 6 typedef struct 7 { 8 char ch; 9 int weight; //權值,這個字元出現的頻率 10 int parent; 11 int left; 12 int right; 13 }HuffNode; 14 15 typedef struct 16 { 17 char code[MAXNUM]; 18 int start; 19}HuffCode; 20 21 HuffNode ht[MAXNUM * 2]; //存放哈夫曼樹 22 23 HuffCode hcd[MAXNUM]; //存放ht陣列中對應的字元的編碼 24 25 int n; //字元的個數 26 27 //初始化哈夫曼樹ht 28 void initHt() 29 { 30 FILE * fp; 31 char ch; 32 int i = 0; 33 //從檔案1.txt中讀出要編碼的字元和權值 34 if ((fp = fopen("d:\\1.txt", "r")) == NULL){ 35 printf("can not open the file 1.txt"); 36 exit(0); 37 } 38 ht[i].left = ht[i].right = ht[i].parent = -1; 39 while ((ch = fgetc(fp)) != EOF){ 40 if (ch == '\n'){ 41 i++; 42 ht[i].left = ht[i].right = ht[i].parent = -1; 43 } 44 else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) 45 ht[i].ch = ch; 46 else if (ch >= '0'&&ch <= '9') 47 ht[i].weight = ht[i].weight * 10 + ch - '0'; 48 } 49 n = i + 1; 50 if (fclose(fp)){ 51 printf("can not close the file 1.txt"); 52 exit(0); 53 } 54 } 55 //構造哈夫曼樹,看成有n棵樹,選擇權值最小的兩棵樹合併 56 void createHuffTree() 57 { 58 59 int i = 0, k; 60 int minI, minJ; 61 int f = 0; 62 minI = minJ = -1; //minI<minJ 63 for (k = n; k<2 * n - 1; k++){ 64 //尋找ht中權值最小且無父結點的兩個結點 65 i = 0; 66 f = 0; 67 while (ht[i].ch != '\0'){ 68 if (ht[i].parent == -1){ 69 if (f == 0){ 70 minI = i; 71 f++; 72 } 73 else if (f == 1){ 74 if (ht[i].weight<ht[minI].weight){ 75 minJ = minI; 76 minI = i; 77 } 78 else 79 minJ = i; 80 f++; 81 } 82 else{ 83 if (ht[i].weight<ht[minI].weight){ 84 minJ = minI; 85 minI = i; 86 } 87 else if (ht[i].weight<ht[minJ].weight) 88 minJ = i; 89 } 90 } 91 i++; 92 } 93 //合併兩個結點 94 ht[k].ch = '#'; 95 ht[k].left = minI; 96 ht[k].right = minJ; 97 ht[k].weight = ht[minI].weight + ht[minJ].weight; 98 ht[k].parent = -1; 99 ht[minI].parent = ht[minJ].parent = k; 100 } 101 } 102 103 //將一個字串反轉 104 void reverse(char *str) 105 { 106 int i, j; 107 char ch; 108 for (i = 0, j = strlen(str) - 1; i<j; i++, j--){ 109 ch = str[i]; 110 str[i] = str[j]; 111 str[j] = ch; 112 } 113 } 114 115 //哈夫曼編碼,通過父節點從下往上找 116 void createHuffCode() 117 { 118 int i, j, length; 119 FILE * fp; 120 for (i = 0; i<n; i++){ 121 length = 0; 122 j = i; 123 //給每個字元進行編碼 124 while (ht[j].parent != -1){ 125 if (ht[ht[j].parent].left == j){ 126 hcd[i].code[length++] = 0 + '0'; 127 } 128 else 129 hcd[i].code[length++] = 1 + '0'; 130 j = ht[j].parent; 131 } 132 133 hcd[i].start = hcd[i].code[length - 1] - '0'; 134 hcd[i].code[length] = '\0'; 135 reverse(hcd[i].code); 136 } 137 //把hcd字元編碼寫入檔案document/code.txt中 138 if ((fp = fopen("d:\\2.txt", "w")) == NULL){ 139 printf("can not open the file 2.txt"); 140 exit(0); 141 } 142 for (i = 0; i<n; i++){ 143 fputc(ht[i].ch, fp); 144 fputs(" ", fp); 145 fputs(hcd[i].code, fp); 146 fputc('\n', fp); 147 } 148 if (fclose(fp)){ 149 printf("can not close the file 2.txt"); 150 exit(0); 151 } 152 } 153 //哈夫曼解碼,每次都從根節點開始搜尋 154 int releaseHuffCode(char *str, char* code) 155 { 156 int root = 2 * n - 2; 157 int length = 0, i = 0; 158 while (code[i]){ 159 if (code[i] == '0' + 0) 160 root = ht[root].left; 161 else if (code[i] == '0' + 1) 162 root = ht[root].right; 163 else 164 return 0; 165 if (ht[root].left == -1 && ht[root].right == -1){ 166 str[length++] = ht[root].ch; 167 root = 2 * n - 2; 168 } 169 i++; 170 } 171 str[length] = '\0'; 172 if (root == 2 * n - 2) 173 return 1; 174 return 0; 175 } 176 177 //使用者輸入編碼字元 178 void encode() 179 { 180 int i = 0, j, f = 1; 181 char str[50]; 182 char code[500] = { '\0' }; 183 printf("\n請輸入要編碼的字串(length<50)\n"); 184 scanf("%s", str); 185 while (str[i]){ 186 if ((str[i] >= 'a'&&str[i] <= 'z') || (str[i] >= 'A'&&str[i] <= 'Z')){ 187 for (j = 0; j<n; j++) 188 if (str[i] == ht[j].ch){ 189 strcat(code, hcd[j].code); 190 break; 191 } 192 i++; 193 } 194 else{ 195 f = 0; 196 break; 197 } 198 } 199 if (f) 200 puts(code); 201 else 202 printf("你輸入的字串錯誤!\n"); 203 printf("按任意鍵後重新選擇!\n"); 204 _getch(); 205 } 206 207 //使用者輸入解碼字串 208 void decode() 209 { 210 char str[50]; 211 char code[500]; 212 printf("\n請輸入要解碼的字串(用0和1表示)\n"); 213 scanf("%s", code); 214 if (releaseHuffCode(str, code)) 215 puts(str); 216 else 217 printf("你輸入的字串錯誤!\n"); 218 219 printf("按任意鍵後重新選擇!\n"); 220 _getch(); 221 } 222 223 //主函式 224 int main() 225 { 226 int choice = 1; 227 initHt(); 228 createHuffTree(); 229 createHuffCode(); 230 while (choice){ 231 system("cls"); 232 printf("/****************哈夫曼編碼與解碼*********************/\n"); 233 printf(" 在H:\\1.txt 檔案中存放著各個字母的權值\n"); 234 printf(" 程式從中讀出各個字母的權值構造哈夫曼樹並進行編碼\n"); 235 printf(" 各個字元的編碼存在H:\\2.txt檔案中\n"); 236 printf("/*****************************************************/\n"); 237 printf("\n請輸入你的選擇:1 ---- 編碼 2 ---- 解碼 0 ---- 退出\n"); 238 scanf("%d", &choice); 239 switch (choice){ 240 case 1: 241 encode(); 242 break; 243 case 2: 244 decode(); 245 break; 246 case 0: 247 printf("謝謝使用!\n"); 248 break; 249 default: 250 choice = 1; 251 printf("你的輸入錯誤!按任意鍵後重新輸入!\n"); 252 _getch(); 253 break; 254 } 255 } 256 }
所需要的兩個檔案:
1.txt(儲存代編碼字元及其頻率)
2.txt(儲存字元的編碼)
執行截圖: