数据结构实验四-赫夫曼编码

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 //////////////////////////////////////////////////////////////////////////////
  5 /*定义赫夫曼树结点的结构体变量,存放结点的权值、字符、双亲、坐孩子和右孩子*/
  6 typedef struct{
  7     int weight;
  8     char ch;                 //增加一个域用于存放该节点的字符
  9     int parent, lchild, rchild;
 10 }HTNode, *HuffmanTree;
 11 typedef char **HuffmanCode; //指向赫夫曼编码的指针
 12 //////////////////////////////////////////////////////////////////////////////
 13 /*本程序用到的函数原型*/
 14 void welcome();    //打印操作选择界面
 15 void HuffmanCoding(HuffmanTree &, char *, int *, int);//建立赫夫曼树的算法
 16 void select(HuffmanTree HT, int j, int *s1, int *s2); //从目前已建好的赫夫曼树中选择parent为0且weight最小的两个结点
 17 void Init(); //输入n个字符及其对应的权值,根据权值建立哈夫曼树
 18 void Coding(); //编码
 19 void Decoding(); //译码
 20 void Print_code(); //打印译码好的代码文件
 21 void Print_tree(); //以凹凸表形式打印哈夫曼树
 22 int Read_tree(HuffmanTree &HT); //从文件中读入赫夫曼树
 23 void find(HuffmanTree &HT, char *code, char *text, int i, int m);//译码时根据01字符串寻找相应叶子节点的递归算法
 24 void Convert_tree(unsigned char T[100][100], int s, int *i, int j);//将内存中的赫夫曼树转换成凹凸表形式的赫夫曼树
 25 HuffmanTree HT; //全局变量,指向存放赫夫曼树的存储空间
 26 int n = 0; //全局变量,存放赫夫曼树叶子结点的数目
 27 int main()
 28 {
 29     char select;
 30     while (1)
 31     {
 32         welcome();
 33         scanf("%c", &select);
 34         switch (select)
 35         {
 36         case 'i':
 37         case 'I':Init(); break;
 38         case 'c':
 39         case 'C':Coding(); break;
 40         case 'd':
 41         case 'D':Decoding(); break;
 42         case 'p':
 43         case 'P':Print_code(); break;
 44         case 't':
 45         case 'T':Print_tree(); break;
 46         case 'e':
 47         case 'E':exit(1);
 48         default:printf("Input error!\n");
 49         }
 50         getchar();
 51     }
 52     return 0;
 53 }
 54 void welcome() //打印操作选择界面
 55 {
 56     printf("*-----------------------------------------------------*\n");
 57     printf("|                What do you want to do?              |\n");
 58     printf("|-----------------------------------------------------|\n");
 59     printf("|                                                     |\n");
 60     printf("| I--------------------------Init the Huffman tree. |\n");
 61     printf("| C--------------------------Code your file.         |\n");
 62     printf("| D--------------------------Decode the code.        |\n");
 63     printf("| P--------------------------Print the codefile.     |\n");
 64     printf("| T--------------------------Print the Huffman tree. |\n");
 65     printf("|                                                     |\n");
 66     printf("*-----------------------------------------------------*\n");
 67 }
 68 //////////////////////////////////////////////////////////////////////////////////////
 69 /*初始化函数,输入n个字符及其对应的权值,根据权值建立哈夫曼树,并将其存于文件hfmtree中*/
 70 void Init()
 71 {
 72     FILE *fp;
 73     int i, n, w[52]; //w数组存放n个字符的权值
 74     char character[52]; //存放n个字符
 75     printf("\n输入字符个数 n:");
 76     scanf("%d", &n);        //输入字符集大小
 77     printf("输入%d个字符及其对应的权值:\n", n);
 78     for (i = 0; i < n; i++)
 79     {
 80         char b = getchar();
 81         scanf("%c", &character[i]);
 82         scanf("%d", &w[i]);           //输入n个字符和对应的权值
 83     }
 84     HuffmanCoding(HT, character, w, n);    //建立赫夫曼树
 85     if ((fp = fopen("hfmtree.txt", "w")) == NULL)
 86         printf("Open file hfmtree.txt error!\n");
 87     for (i = 1; i <= 2 * n - 1; i++)
 88     {
 89         if (fwrite(&HT[i], sizeof(HTNode), 1, fp) != 1)   //将建立的赫夫曼树存入文件hfmtree.txt中
 90             printf("File write error!\n");
 91     }
 92     printf("\n建立赫夫曼树成功,已将其存于文件hfmtree.txt中\n");
 93     fclose(fp);
 94 }
 95 ///////////////////////////////////////////////////////////////////////////////////
 96 //////建立赫夫曼树的算法///////////////////////////////////////////////////////////
 97 void HuffmanCoding(HuffmanTree &HT, char *character, int *w, int n)
 98 {
 99     int m, i, s1, s2;
100     HuffmanTree p;
101     if (n <= 1) return;
102     m = 2 * n - 1;
103     HT = (HuffmanTree) malloc((m + 1)*sizeof(HTNode));
104     for (p = HT + 1, i = 1; i <= n; ++i, ++p, ++character, ++w)
105     {
106         p->ch = *character; 
107         p->weight = *w; 
108         p->parent = 0; 
109         p->lchild = 0; 
110         p->rchild = 0;
111     }
112     for (; i <= m; ++i, ++p) 
113     { 
114         p->ch = 0; 
115         p->weight = 0; 
116         p->parent = 0;
117         p->lchild = 0;
118         p->rchild = 0; }
119     for (i = n + 1; i <= m; ++i)
120     {
121         select(HT, i - 1, &s1, &s2);
122         HT[s1].parent = i;
123         HT[s2].parent = i;
124         HT[i].lchild = s1; 
125         HT[i].rchild = s2;
126         HT[i].weight = HT[s1].weight + HT[s2].weight;
127     }
128 }
129 ///////////////////////////////////////////////////////////////////////////////
130 /*从HT[1]到HT[j]中选择parent为0且weight最小的两个结点,用s1和s2返回其序号*/
131 void select(HuffmanTree HT, int j, int *s1, int *s2)
132 {
133     int i;
134     //找weight最小的结点
135     for (i = 1; i <= j; i++)
136         if (HT[i].parent == 0)
137         {
138             *s1 = i; 
139             break;
140         }
141         for (; i <= j; i++)
142             if ((HT[i].parent == 0) && (HT[i].weight < HT[*s1].weight))
143                 *s1 = i;
144         HT[*s1].parent = 1;
145         //找weight次小的结点
146         for (i = 1; i <= j; i++)
147             if (HT[i].parent == 0)
148             {
149                 *s2 = i; break;
150             }
151             for (; i <= j; i++)
152                 if ((HT[i].parent == 0) && (i != *s1) && (HT[i].weight < HT[*s2].weight))
153                     *s2 = i;
154 }
155 ///////////////////////////////////////////////////////////////////////////////
156 /*对文件tobetrans中的正文进行编码,然后将结果存入文件codefile中*/
157 void Coding()
158 {
159     FILE *fp, *fw;
160     int i, f, c, start;
161     char *cd;
162     HuffmanCode HC;
163     if (n == 0)
164         n = Read_tree(HT);//从文件hfmtree.txt中读入赫夫曼树,返回叶子结点数
165     /////以下程序段求赫夫曼树中各叶子节点的字符对应的的编码,并存于HC指向的空间中
166     {
167         HC = (HuffmanCode) malloc((n + 1)*sizeof(char*) );
168         cd = (char *) malloc(n*sizeof(char) );
169         cd[n - 1] = '\0';
170         for (i = 1; i <= n; ++i)
171         {
172             start = n - 1;
173             for (c = i, f = HT[i].parent; f != 0; c = f, f = HT[f].parent)
174                 if (HT[f].lchild == c)
175                     cd[--start] = '0';
176                 else cd[--start] = '1';
177                 HC[i] = (char *) malloc((n - start)*sizeof(char) );
178                 strcpy(HC[i], &cd[start]);
179         }
180         free(cd);
181     }
182     /////////////////////////////////////////////////////////////////////////////////////
183     if ((fp = fopen("tobetrans.txt", "rb")) == NULL)
184         printf("Open file tobetrans.txt error!\n");
185     if ((fw = fopen("codefile.txt", "wb+")) == NULL)
186         printf("Open file codefile.txt error!\n");
187     char temp;
188     fscanf(fp, "%c", &temp); //从文件读入第一个字符
189     while (!feof(fp))
190     {
191         for (i = 1; i <= n; i++)
192             if (HT[i].ch == temp) break;    //在赫夫曼树中查找字符所在的位置
193         for (int r = 0; HC[i][r] != '\0'; r++) //将字符对应的编码存入文件
194             fputc(HC[i][r], fw);
195         fscanf(fp, "%c", &temp);        //从文件读入下一个字符
196     }
197     fclose(fw);
198     fclose(fp);
199     printf("\n对文件hfmtree.txt编码成功,结果已存入codefile.txt中。\n\n");
200 }
201 
202 /////////////////////////////////////////////////////////////////////////
203 /*将文件codefile中的代码进行译码,结果存入文件textfile中*/
204 void Decoding()
205 {
206     FILE *fp, *fw;
207     int m, i;
208     char *code, *text, *p;
209     if (n == 0)
210         n = Read_tree(HT);//从文件hfmtree.txt中读入赫夫曼树,返回叶子结点数
211     if ((fp = fopen("codefile.txt", "rb")) == NULL)
212         printf("Open file codefile.txt error!\n");
213     if ((fw = fopen("textfile.txt", "wb+")) == NULL)
214         printf("Open file textfile.txt error!\n");
215     code = (char *) malloc(sizeof(char) );
216     fscanf(fp, "%c", code);        //从文件读入一个字符
217     for (i = 1; !feof(fp); i++)
218     {
219         code = (char *) realloc(code, (i + 1)*sizeof(char) ); //增加空间
220         fscanf(fp, "%c", &code[i]);     //从文件读入下一个字符 
221     }
222     code[i - 1] = '\0';
223     /////////到此codefile.txt文件中的字符已全部读入,存放在code数组中
224     text = (char *) malloc(100 * sizeof(char) );
225     p = text;
226     m = 2 * n - 1;
227     if (*code == '0')
228         find(HT, code, text, HT[m].lchild, m);   //从根节点的左子树去找
229     else
230         find(HT, code, text, HT[m].rchild, m);   //从根节点的右子树去找
231     for (i = 0; p[i] != '\0'; i++) //把译码好的字符存入文件textfile.txt中
232         fputc(p[i], fw);
233     fclose(fp);
234     fclose(fw);
235     printf("\n对codefile.txt文件译码成功,结果已存入textfile.txt文件。\n\n");
236 }
237 //////////////////////////////////////////////////////////////////////////////////////////////////////
238 /*将文件codefi1e以紧凑格式显示在终端上,每行50个代码。同时将此字符形式的编码文件写入文件codeprint中。*/
239 void Print_code()
240 {
241     FILE *fp, *fw;
242     char temp;
243     int i;
244     if ((fp = fopen("codefile.txt", "rb")) == NULL)
245         printf("Open file codefile.txt error!\n");
246     if ((fw = fopen("codeprint.txt", "wb+")) == NULL)
247         printf("Open file codeprint.txt error!\n");
248     printf("\n文件codefi1e以紧凑格式显示如下:\n");
249     fscanf(fp, "%c", &temp);        //从文件读入一个字符
250     for (i = 1; !feof(fp); i++)
251     {
252         printf("%c", temp);
253         if (i % 50 == 0) printf("\n");
254         fputc(temp, fw);   //将该字符存入文件codeprint.txt中
255         fscanf(fp, "%c", &temp);        //从文件读入一个字符
256     }
257     printf("\n\n此字符形式的编码已写入文件codeprint.txt中.\n\n");
258     fclose(fp);
259     fclose(fw);
260 }
261 //////////////////////////////////////////////////////////////////////////////////////////////////
262 /*将已在内存中的哈夫曼树以凹凸表形式显示在屏幕上,同时将此字符形式的哈夫曼树写入文件treeprint中。*/
263 void Print_tree()
264 {
265     unsigned char T[100][100];
266     int i, j, m = 0;
267     FILE *fp;
268     if (n == 0)
269         n = Read_tree(HT);//从文件hfmtree.txt中读入赫夫曼树,返回叶子结点数
270 
271     Convert_tree(T, 0, &m, 2 * n - 1); //将内存中的赫夫曼树转换成凹凸表形式的树,存于数组T中
272     if ((fp = fopen("treeprint.txt", "wb+")) == NULL)
273         printf("Open file treeprint.txt error!\n");
274     printf("\n以凹凸表形式打印已建好的赫夫曼树:\n");
275     for (i = 1; i <= 2 * n - 1; i++)
276     {
277         for (j = 0; T[i][j] != 0; j++)
278         {
279             if (T[i][j] == ' ') { printf(" "); fputc(T[i][j], fp); }
280             else
281             {
282                 printf("%d", T[i][j]); fprintf(fp, "%d\n", T[i][j]);
283             }
284         }
285         printf("\n");
286     }
287     fclose(fp);
288     printf("\n此字符形式的哈夫曼树已写入文件treeprint.txt中.\n\n");
289 
290 }
291 //////////////////////////////////////////////////////////////////////////////////
292 /*从文件hfmtree.txt中读入赫夫曼树,返回叶子节点数*/
293 int Read_tree(HuffmanTree &HT)
294 {
295     FILE *fp;
296     int i, n;
297     HT = (HuffmanTree) malloc(sizeof(HTNode));
298     if ((fp = fopen("hfmtree.txt", "r")) == NULL)
299         printf("Open file hfmtree.txt error!\n");
300     for (i = 1; !feof(fp); i++)
301     {
302         HT = (HuffmanTree) realloc(HT, (i + 1)*sizeof(HTNode)); //增加空间
303         fread(&HT[i], sizeof(HTNode), 1, fp); //读入一个节点信息
304     }
305     fclose(fp);
306     n = (i - 1) / 2;
307     return n;
308 }
309 ////////////////////////////////////////////////////////////////
310 /*译码时根据01字符串寻找相应叶子节点的递归算法*/
311 void find(HuffmanTree &HT, char *code, char *text, int i, int m)
312 {
313 
314     if (*code != '\0') //若译码未结束
315     {
316         code++;
317         if (HT[i].lchild == 0 && HT[i].rchild == 0)   //若找到叶子节点
318         {
319             *text = HT[i].ch; //将叶子节点的字符存入text中
320             text++;
321             if ((*code == '0'))
322                 find(HT, code, text, HT[m].lchild, m); //继续从根节点的左子树找
323             else
324                 find(HT, code, text, HT[m].rchild, m); //继续从根节点的右子树找
325         }
326         else   //如果不是叶子节点
327             if (*code == '0')
328                 find(HT, code, text, HT[i].lchild, m);   //从该节点的左子树去找
329             else
330                 find(HT, code, text, HT[i].rch
331                 
332                 
333                 
334                 
335                 
336                 
337                 
338                 
339                 
340                 
341                 
342                 
343                 
344                 
345                 
346                 
347                 
348                 
349                 
350                 
351                 
352                 ------------------ild, m);   //从该节点的右子树去找
353 
354     }
355     else
356         *text = '\0'; //译码结束
357 }
358 ////////////////////////////////////////////////////////////////////////
359 /*将内存中的赫夫曼树转换成凹凸表形式的赫夫曼树*/
360 void Convert_tree(unsigned char T[100][100], int s, int *i, int j)
361 {
362     int k, l;
363     l = ++(*i);
364     for (k = 0; k < s; k++)
365         T[l][k] = ' ';
366     T[l][k] = HT[j].weight;
367     if (HT[j].lchild)
368         Convert_tree(T, s + 1, i, HT[j].lchild);
369     if (HT[j].rchild)
370         Convert_tree(T, s + 1, i, HT[j].rchild);
371     T[l][++k] = '\0';
372 }

 

posted @ 2013-11-24 20:49  herizai007  阅读(540)  评论(0编辑  收藏  举报