最近完成了數據結構課程設計,被分到的題目是《哈夫曼編碼和解碼》,現在在這篇博文里分享一下自己的成果。
我在設計時,在網上參考了很多老師和前輩的算法和代碼,向他們表示感謝!他們的成果給了我很多啟示和幫助。另外,自己的成品中也還有很多不完善的地方,歡迎批評指正。
課題:哈夫曼編碼與解碼 C++代碼實現
(1)統計某電文中字符出現的頻率(假設電文中只含有大小寫英文字母,以及逗號和點號);
(2)把字符出現的頻率作為權值建立哈夫曼樹,進行哈夫曼編碼,並輸出每個字符的編碼結果;
(3)對電文進行哈夫曼編碼;
(4)把電文的哈夫曼編碼進行譯碼,輸出對應電文的內容。
1 #include <stdlib.h> 2 #include <stdio.h> 3 #include <malloc.h> 4 #include <string.h> 5 #include <ctype.h> 6 #define MAX 999999 //一個極大值 7 #define NUM 10 8 9 //存儲哈夫曼樹每個結點 10 typedef struct Node { 11 char ch; 12 int weight; //權值 13 int parent; 14 int lchild,rchild; 15 }HFNode; 16 //存儲每個字符及其哈夫曼編碼 17 typedef struct { 18 char ch; 19 char code[NUM]; 20 }HFCharCode; 21 22 HFNode HT[28*2-1]; //哈夫曼樹結構體 23 HFCharCode HCD[28]; //哈夫曼編碼結構體 24 int LeafNum; //葉子結點數 25 int NodeNum; //所有結點數 26 char EnterStr[MAX]; //輸入的待編碼電文 27 char EnterCode[MAX]; //輸入的待解碼密文 28 char RealStr[MAX]; //密文解碼后的電文 29 int AllWeight[28]; //存儲所有28個字符的權值 30 31 void Statistics(); 32 void CreateHFTree(); 33 void SelectMin(int &min1, int &min2); 34 void CreateHFCode(); 35 void ReverseStr(char *str); 36 void EncodeStr(); 37 void DecodeHFCode(); 38 39 int main() { 40 printf("****** 哈夫曼編碼與解碼 ******\n\n"); 41 printf("*** 輸入一串字符串 ***\n"); 42 scanf("%s", EnterStr); 43 getchar(); 44 Statistics(); 45 CreateHFTree(); 46 CreateHFCode(); 47 EncodeStr(); 48 printf("\n*** 輸入想解碼的內容 ***\n"); 49 scanf("%s", EnterCode); 50 getchar(); 51 DecodeHFCode(); 52 return 0; 53 } 54 55 //統計每個字符權值 56 void Statistics() { 57 int len = strlen(EnterStr); 58 for(int i = 0; i <= 27; i++) 59 AllWeight[i] = 0; 60 for(int j = 0; j <= len - 1; j++) { 61 if(isalpha(EnterStr[j])) { 62 EnterStr[j] = tolower(EnterStr[j]); 63 AllWeight[EnterStr[j]-'a']++; 64 } 65 else if((int)EnterStr[j] == 44) 66 AllWeight[26]++; 67 else if((int)EnterStr[j] == 46) 68 AllWeight[27]++; 69 else { 70 printf("\n輸入不符合要求!\n\n"); 71 exit(-1); 72 } 73 } 74 int i = 0, j = 0; 75 for( ; i <= 25; i++) { 76 if(AllWeight[i] != 0) { 77 HT[j].weight = AllWeight[i]; 78 HT[j].ch = i+'a'; 79 j++; 80 } 81 } 82 if(AllWeight[i] != 0) { 83 HT[j].weight = AllWeight[i]; 84 HT[j].ch = ','; 85 j++; 86 i++; 87 } 88 if(AllWeight[i] != 0) { 89 HT[j].weight = AllWeight[i]; 90 HT[j].ch = '.'; 91 } 92 printf("\n*** 打印每個字符的權值 ***\n"); 93 int n = 0; 94 for(int i = 0; i <= 27; i++) { 95 if(AllWeight[i] != 0) { 96 n++; 97 if(i <= 25) 98 putchar('a'+i); 99 else if(i == 26) 100 printf(","); 101 else 102 printf("."); 103 printf(": %d\n", AllWeight[i]); 104 } 105 } 106 LeafNum = n; 107 NodeNum = 2*LeafNum-1; 108 } 109 110 //構造哈夫曼樹 111 void CreateHFTree() { 112 int i; 113 for(i = 0; i <= LeafNum-1; i++) { 114 HT[i].parent = -1; 115 HT[i].lchild = -1; 116 HT[i].rchild = -1; 117 HT[i].weight = HT[i].weight; 118 } 119 for(; i <= NodeNum-1; i++) { 120 HT[i].parent = -1; 121 HT[i].lchild = -1; 122 HT[i].rchild = -1; 123 HT[i].weight = MAX; 124 } 125 int min1, min2; 126 for(i = LeafNum; i <= NodeNum-1; i++) { 127 SelectMin(min1, min2); 128 HT[min1].parent = i; 129 HT[min2].parent = i; 130 HT[i].lchild = min1; 131 HT[i].rchild = min2; 132 HT[i].weight = HT[min1].weight + HT[min2].weight; 133 } 134 // printf("\n*** 打印哈夫曼樹 ***\n"); 135 // for(int i = 0; i <= NodeNum-1; i++) { 136 // printf("序號:%d 字符:%c 權值:%d 雙親:%d 左孩:%d 右孩:%d\n", i, HT[i].ch, HT[i].weight, HT[i].parent, HT[i].lchild, HT[i].rchild); 137 // } 138 } 139 //找到兩個權值最小的二叉樹的序號 140 void SelectMin(int &min1, int &min2) { 141 int i = 0; 142 int temp; 143 int wetmin1, wetmin2; 144 while(HT[i].parent != -1) 145 i++; 146 wetmin1 = HT[i].weight; 147 min1 = i; 148 i++; 149 while(HT[i].parent != -1) 150 i++; 151 wetmin2 = HT[i].weight; 152 min2 = i; 153 i++; 154 if(wetmin1 > wetmin2) { 155 temp = wetmin2; 156 wetmin2 = wetmin1; 157 wetmin1 = temp; 158 temp = min2; 159 min2 = min1; 160 min1 = temp; 161 } 162 for(; i <= NodeNum-1; i++) { 163 if(HT[i].weight < wetmin1 && HT[i].parent == -1) { 164 wetmin2 = wetmin1; 165 wetmin1 = HT[i].weight; 166 min2 = min1; 167 min1 = i; 168 } else if(HT[i].weight < wetmin2 && HT[i].parent == -1) { 169 wetmin2 = HT[i].weight; 170 min2 = i; 171 } 172 } 173 } 174 175 //進行哈夫曼編碼 176 void CreateHFCode() { 177 int i, j, len; 178 for(i = 0; i <= LeafNum-1; i++) { 179 len = 0; 180 j = i; 181 HCD[i].ch = HT[j].ch; 182 while(HT[j].parent != -1) { //不是根節點 183 if(HT[HT[j].parent].lchild == j) { //是雙親結點的左孩子 184 HCD[i].code[len++] = '0'+0; //加上字符0 185 }else //是右孩子 186 HCD[i].code[len++] = '0'+1; //加上字符1 187 j = HT[j].parent; //往上遍歷 188 } 189 HCD[i].code[len] = '\0'; //字符串末尾 190 ReverseStr(HCD[i].code); 191 } 192 printf("\n*** 打印每個字符的編碼 ***\n"); 193 for(int i = 0; i <= LeafNum-1; i++) 194 printf("%c: %s\n", HT[i].ch, HCD[i].code); 195 } 196 //將一個字符串反轉 197 void ReverseStr(char *str) { 198 int i, j; 199 char c; 200 for(i = 0, j = strlen(str)-1; i < j; i++, j--) { 201 c = str[i]; 202 str[i] = str[j]; 203 str[j] = c; 204 } 205 } 206 207 //哈夫曼編碼 208 void EncodeStr() { 209 int len = strlen(EnterStr); 210 printf("\n*** 編碼結果 ***\n"); 211 for(int i = 0; i <= len-1; i++) { 212 for(int j = 0; j <= LeafNum-1; j++) { 213 if(EnterStr[i] == HCD[j].ch) 214 printf("%s", HCD[j].code); 215 } 216 } 217 printf("\n"); 218 } 219 220 //哈夫曼解碼 221 void DecodeHFCode() { 222 int k = NodeNum-1; //根結點序號, 開始時一定在最后一個 223 int len = 0, i = 0; 224 while(EnterCode[i]) { 225 if(EnterCode[i] == '0'+0) 226 k = HT[k].lchild; 227 else if(EnterCode[i] == '0'+1) 228 k = HT[k].rchild; 229 else { 230 printf("\n錯誤! 密文中僅能含有1和0!\n\n"); 231 exit(-1); 232 } 233 if(HT[k].lchild == -1 && HT[k].rchild == -1) { 234 RealStr[len++] = HT[k].ch; 235 k = NodeNum-1; 236 } 237 i++; 238 } 239 RealStr[len] = '\0'; 240 if(k == NodeNum-1) { 241 printf("\n*** 解碼結果 ***\n%s\n\n", RealStr); 242 exit(0); 243 } 244 printf("\n錯誤! 部分密文無法解密!\n\n"); 245 exit(-1); 246 }