实验二二叉树的遍历及霍夫曼编码班级:计科1101班学号:0909101605姓名:杜茂鹏2013年5月22日一、实验目的掌握二叉树的建立及遍历操作,霍夫曼编码基本操作及存储结构表示二、实验内容1. 系统要求包含以下功能1)初始化:从终端读入字符集大小n,以及n个字符和n个权值(或者读入字符集和频度数据文件),建立哈夫曼树,并将哈夫曼树存入到文件HfmTree 中。
2)编码:利用已建好的哈夫曼树(如果不在内存中,则从文件中读入),从文件ToBeTran中读入原文,对原文进行编码,将编码后的结果存入文件CodeFile 中。
3)译码:利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件TextFile中。
4)打印:打印输出哈夫曼树,显示ToBeTran, TextFile和CodeFile文件的内容。
三、实验要求1.在上机前写出全部源程序;2.能在机器上正确运行程序;3.用户界面友好。
四、概要设计1)首先动态分配数组存储霍夫曼树及存储霍夫曼编码表,然后从终端或文件读入霍夫曼树的字符变量及其频度,初始化建立霍夫曼树并将其写入文件HfmTree.txt中。
2)从指定的文件succe.txt中读入原文,利用已经编好的霍夫曼树对其编码,将编码结果写入文件Coding.txt保存。
3)利用已建好的哈夫曼树将文件Coding.txt中的代码进行译码,结果存入文件decoding.txt中。
五、测试数据:2.原文内容“THIS IS MY PROGRAM”六、详细设计实验内容(原理、操作步骤、程序代码)//建立霍夫曼树,对原文进行编码、译码#include<stdio.h>#include<stdlib.h>#include<malloc.h>#include<string.h>typedef struct tree{char ch;int weight;//权值int parent,lchild,rchild;}HTNode,*HuffmanTree;//动态分配数组存储霍夫曼树typedef char **HuffmanCode;//动态分配数组存储霍夫曼编码表void Select(HuffmanTree &HT,int* s1,int* s2,int n){int j;int min1=10000;for(j=1;j<=n;j++){if(HT[j].parent==0&&min1>HT[j].weight){min1=HT[j].weight;*s1=j;}}int min2=10000;for(j=1;j<=n;j++){if(HT[j].parent==0&&min2>HT[j].weight&&j!=*s1){min2=HT[j].weight;*s2=j;}}}void HuffmanBiTree(HuffmanTree &HT,char *c,int *w,int n)//w存放n个字符的权值(>0),构造霍夫曼树HT,并求出n个字符的霍夫曼编码HC{int i;if(n<=1)return;int m=2*n-1;//霍夫曼树的结点数HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//0号单元未用for(i=1;i<=n;++i){HT[i].ch=c[i-1];HT[i].weight=w[i-1];HT[i].lchild=0;HT[i].parent=0;HT[i].rchild=0;}for(;i<=m;++i){HT[i].ch=NULL;HT[i].weight=0;HT[i].lchild=0;HT[i].parent=0;HT[i].rchild=0;}for(i=n+1;i<=m;i++)//建霍夫曼树{int s1,s2;Select(HT,&s1,&s2,i-1);//在HT[1..i-1]选择parent为0且weight最小的两个结点,其序号分别为s1和s2HT[s1].parent=i;HT[s2].parent=i;HT[i].lchild=s1;HT[i].rchild=s2;HT[i].weight=HT[s1].weight+HT[s2].weight;}FILE *IN;if((IN=fopen("Hfmtree.txt","w+"))==NULL){printf("file open error!");exit(1);}for(int i=1;i<=2*n-1;i++){fprintf(IN,"%c,%d,%d,%d,%d",HT[i].ch,HT[i].weight,HT[i].parent,HT[i].lchild, HT[i].rchild);//将结构体数组HT对应的值按照%c%d..格式输入到文件中fputs("\n",IN); //在文件中输入换行符}fclose(IN);}//从叶子到根逆向求每个字符的霍夫曼编码void HuffmanBicode(HuffmanCode &HC,int n,HuffmanTree &HT){HC=(HuffmanCode)malloc((n+1)*sizeof(char *));//分配n个字符编码的头指针向量char *cd=(char *)malloc(n*sizeof(char)); //分配求编码的工作空间cd[n-1]='\0'; //编码结束符int i;for(i=1;i<=n;++i) //逐个字符求霍夫曼编码{int start=n-1; //编码结束位置for(int c=i,f=HT[i].parent;f!=0;c=f,f=HT[c].parent)//从叶子到根逆向求编码if(HT[f].lchild==c)cd[--start]='0';elsecd[--start]='1';HC[i]=(char*)malloc((n-start)*sizeof(char));//为第i个字符编码分配空间strcpy(HC[i],&cd[start]); //复制}free(cd);}void CodingFile(HuffmanTree &HT,HuffmanCode &HC,int n){char ch;int i;FILE *IN,*OUT; //两个文件指针,分别指向打开文件和生成文件if ((IN = fopen("success.txt", "r")) == NULL){printf("File Open Error!\n");exit(1);}if ((OUT = fopen("coding.txt", "w+")) == NULL){printf("File Open Error!\n");exit(1);}while (!feof(IN)) //如果文件结束,feof返回1,否则返回0{ch = fgetc(IN); //依次得到文件中的字符i = 1;while ((HT[i].ch != ch) && (i <= n)){i++;if (i > n)return;}/* 将ch代表的字符的编码写入到输出文件*/fputs(HC[i], OUT);}fclose(IN); //关闭文件fclose(OUT);}void Decoding(HuffmanTree &HT,int n) //译码函数{char sh;FILE *P1,*P2,*P3;if((P1=fopen("coding.txt","r"))==NULL){printf("File open error");exit(1);}if((P2=fopen("decoding.txt","w+"))==NULL){printf("File open error");exit(1);}int f=2*n-1;while (!feof(P1)){sh=fgetc(P1);printf("%c",sh);if(sh=='1') //读到字符为1时,在霍夫曼树中表示为它的右孩子f=HT[f].rchild;if(sh=='0') //读到字符为0时,在霍夫曼树中表示为它的左孩子f=HT[f].lchild;if(!HT[f].lchild) //当读到的结点为叶子结点时,将叶子结点代表的字符输出{fputc(HT[f].ch,P2);f=2*n-1;}}fclose(P1);fclose(P2);}void menu(){printf("1.建立霍夫曼树");printf("2.编码\n");printf("3.译码\n");printf("0.结束\n");}int main(void){int n,i,m;HuffmanTree HT;HuffmanCode HC;do{menu();printf("你想要进行的操作:\n");scanf("%d",&n);switch(n){case 1:{printf("请输入字符集大小:");scanf("%d",&m);char *Charset=(char *)malloc(m*sizeof(char)); //为字符集分配内存printf("请输入%d个字符:",m);getchar();for(i=0;i<m;i++){scanf("%c",&Charset[i]);getchar();}int *weight=(int*)malloc(m*sizeof(int)); //为权值数组分配内存printf("输入对应的%d个权值:",m);for(i=0;i<m;i++){scanf("%d",&weight[i]);getchar();}HuffmanBiTree(HT,Charset,weight,m);}break;case 2:{printf("字符集的大小:");scanf("%d",&m);FILE *fp;if((fp=fopen("Hfmtree.txt","r"))==NULL){printf("file open error");exit(1);}int i;HT=(HuffmanTree)malloc((2*m)*sizeof(HTNode));//需从文件中读入已经写好的霍夫曼树for(i=1;i<=2*m-1;i++){fscanf(fp,"%c,%d,%d,%d,%d",&HT[i].ch,&HT[i].weight,&HT[i].parent,&HT[i]. lchild,&HT[i].rchild);//将文件中字符和整型数据赋给结构体数组HTfgetc(fp); //消除霍夫曼树文件中的换行符}HuffmanBicode(HC,m,HT);CodingFile(HT,HC,m);break;}case 3:{printf("字符集的大小:");scanf("%d",&m);FILE *fp;if((fp=fopen("Hfmtree.txt","r"))==NULL){printf("file open error");exit(1);}int i;HT=(HuffmanTree)malloc((2*m)*sizeof(HTNode));for(i=1;i<=2*m-1;i++){fscanf(fp,"%c,%d,%d,%d,%d",&HT[i].ch,&HT[i].weight,&HT[i].parent,&HT[i]. lchild,&HT[i].rchild);fgetc(fp);}Decoding(HT,m);break;}case 0:break;default:break;}}while(n!=0);system("pause");return 0;}调试分析:此为开始界面,选择你要进行的操作的编号(我们应该先建立霍夫曼树): 此时霍夫曼树已经建成,可以关闭窗口并在hfmtree.text里查看.接下来我们要对从文件读入的字符进行编码,先在当前目录下新建一个名为success.txt的文本,输入要进行编码的字符:THIS IS MYPROGRAM然后再次运行程序,选择操作2:此时编码已经完成并已经存入了coding.txt中,打开coding.txt可以看到:此时可以进行译码操作:再次打开程序,选择操作3:此时译码结果已经存在于decoding.txt中,打开译码文件,此时已经实现大部分功能.八、实验心得通过这次上机实验熟练了文件的操作,掌握fgetc(fp),fscanf(fp)的区别,fputc(fp)和fprintf(fp)的区别…注意细节,像拼写错误节应该避免,尽量用通俗易懂的标示符定义函数、变量等,特别注意变量应该先定义再使用。