分析方案哈夫曼编译码系统与实现.docx
《分析方案哈夫曼编译码系统与实现.docx》由会员分享,可在线阅读,更多相关《分析方案哈夫曼编译码系统与实现.docx(14页珍藏版)》请在冰豆网上搜索。
分析方案哈夫曼编译码系统与实现
数据结构实验报告
学号
0928724099
姓名
李晓松
年级
2009
班级
网络工程5班
机号:
学院机房
时间
周三下午2点半-4点
周四上午8点-10点
指导教师
张磊
实验题目:
理解哈夫曼树的特征及其应用;在对哈夫曼树进行理解的基础上,构造哈夫曼树,并用构造的哈夫曼树进行编码和译码;通过该实验,对数据结构的应用有更深层次的理解。
实验要求:
1.问题描述:
利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。
但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码<复原)。
对于双工信道<即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。
试为这样的信息收发站设计一个哈夫曼编/译码系统。
2.一个完整的系统应具有以下功能:
1)初始化从数据文件DataFile.dat中读入字符及每个字符的权值,建立哈夫曼树HuffTree;2)编码用已建好的哈夫曼树,对文件ToBeTran.dat中的文本进行编码形成报文,将报文写在文件Code.txt中;3)译码利用已建好的哈夫曼树,对文件CodeFile.dat中的代码进行译码形成原文,结果存入文件Textfile.txt中;4)输出输出ToBeTran.dat及其报文Code.txt;输出CodeFile.dat及其原文Textfile.txt;[备注]:1)数据文件DataFile.dat中,元素类型为<字符,权值),DataFile.dat的建立可以根据用户输入的一段原文,通过统计出现的字符及各字符出现的次数<与出现的概率作用相同)而得到。另:为了使输出的哈夫曼树不太大,规定报文中只能出现A-H的8个字符,当然对程序稍加修改就可以对出现的所有可输和字符进行处理。2)ToBeTran.dat中的内容由用户在程序执行时从键盘随机输入得到;3)CodeFile.dat中的内容由用户在程序执行时从键盘随机输入得到;DataFile.dat、ToBeTran.dat、CodeFile.dat在程序运行时建立是为了保证编/译码系统的通用性;三.总的设计思想,及环境语言、工具等总的设计思想:1.根据实验要求,先创建文件DataFile.dat、ToBeTran.dat和CodeFile.dat;用下面三个函数实现;voidcreatDataFile(>;//创建数据文件voidcreatToBeTran(>;//创建原文文件voidcreatCodeFile(>;//创建报文文件2.从文件DataFile.dat中读出各字符及相应的权值,创建哈夫曼树HT;3.根据构造的哈夫曼树,求相应字符的哈夫曼编码;4.从文件ToBeTran.dat中读出要翻译的原文,将其翻译成译文存入文件Code.txt中5.从文件CodeFile.dat中读出要翻译的报文,将其翻译成原文存入文件Textfile.txt中环境语言:Windows下的MicrosoftVC++四、数据结构与模块说明下面是编译码系统中所用的数据结构。在这个系统中,哈夫曼树的存储结构采用顺序存储;其结点结构为:程序中用到的头文件、类型定义及主要的函数原型如下:#include"stdio.h"#include"malloc.h"#include"string.h"#definechar_num8typedefstruct{charch。intw。}DFileNode。//定义数据文件的元素类型typedefstruct//赫夫曼树和赫夫曼编码的存储表示{charch。//相应字符intweight。//字符的权值intparent,lchild,rchild。//双亲、左、右孩子指针<下标)char*next。//指向该字符的编码的指针}HTNode,*HuffmanTree。//动态分配数组存储赫夫曼树HuffmanTreeHT。voidcreatDataFile(>//创建数据文件voidcreatToBeTran(>//创建原文文件voidcreatCodeFile(>//创建报文文件intmin(HuffmanTreet,inti>;//求无双亲且权值最小的结点,函数voidselect(>调用voidselect(HuffmanTreet,inti,int&s1,int&s2>;//s1为最小的两个值中序号小的那个voidprint_huff_tree(HuffmanTreeHT,intn>;//输出哈夫曼树,在必要时调用以验证算法的正确性voidcreatHuffmanTree(HuffmanTree&HT,intn>;//创建含n叶子结点的哈夫曼树,字符及权值在文件DataFile.dat中即初始化voidHuffmanCoding(HuffmanTree&HT,intn>;//对有n个叶子结点的哈夫曼树上的叶子结点进行编码voidEnCoding(>//编码:对文件ToBeTran.dat中的文本进行编码,放在文件Code.txt中voidDecoding(>;//译码:对文件CodeFile.dat中的报文进行译码,放在文件Textfile.txt中voidoutput(>;//输出原文和对应的译文;输出报文和对应的原文;五、主要算法的设计与实现voidcreatHuffmanTree(HuffmanTree&HT,intn>{//创建含n叶子结点的哈夫曼树,字符及权值在文件DataFile.dat中即初始化InitialzationFILE*f1。intm,i,s1,s2。HuffmanTreep。DFileNodes。//从文件中读数据时用;m=2*n-1。f1=fopen("DataFile.dat","rb">。HT=(HuffmanTree>malloc((m+1>*sizeof(HTNode>>。//0号单元未用printf("字符及相应的权值为:">。for(p=HT+1,i=1。i<=n。++i,++p>{fread(&s,1,sizeof(DFileNode>,f1>。//从文件中读一个数给s,构造叶子printf("(%c,%d>",s.ch,s.w>。(*p>.ch=s.ch。(*p>.weight=s.w。(*p>.parent=0。(*p>.lchild=0。(*p>.rchild=0。(*p>.next=NULL。}fclose(f1>。printf("\n">。for(。i<=m。++i,++p>{(*p>.ch=''。(*p>.parent=0。(*p>.lchild=0。(*p>.rchild=0。(*p>.next=NULL。}for(i=n+1。i<=m。++i>//建n-1个分支结点{//在HT[1~i-1]中选择parent为0且weight最小的两个结点,其序号分别为s1和s2select(HT,i-1,s1,s2>。HT[s1].parent=HT[s2].parent=i。HT[i].lchild=s1。HT[i].rchild=s2。HT[i].weight=HT[s1].weight+HT[s2].weight。}}voidHuffmanCoding(HuffmanTree&HT,intn>//对有n个叶子结点的哈夫曼树上的叶子结点进行编码{char*cd。inti,start,c,f。cd=(char*>malloc(n*sizeof(char>>。//分配求编码的工作空间cd[n-1]='\0'。//编码结束符for(i=1。i<=n。i++>{//逐个字符求赫夫曼编码start=n-1。//编码结束符位置for(c=i,f=HT[i].parent。f!=0。c=f,f=HT[f].parent>//从叶子到根逆向求编码if(HT[f].lchild==c>cd[--start]='0'。elsecd[--start]='1'。HT[i].next=(char*>malloc((n-start>*sizeof(char>>。//为第i个字符编码分配空间strcpy(HT[i].next,&cd[start]>。//从cd复制编码(串>到HC}free(cd>。//释放工作空间}voidEnCoding(>//编码:对文件ToBeTran.dat中的文本进行编码,放在文件Code.txt中{FILE*f1,*f2。charc。ints,i。f1=fopen("ToBeTran.dat","r">。//打开文件ToBeTran.dat为了读f2=fopen("Code.txt","w">。//打开文件Code.txt为了写//printf("原文:">。while(fread(&c,1,sizeof(char>,f1>>{i=1。while(HT[i].ch!=c>i++。//printf("%c",c>。fputs(HT[i].next,f2>。}//printf("\n">。fclose(f1>。fclose(f2>。//f2=fopen("Code.txt","r">。//printf("译文:">。//while(fread(&c,1,sizeof(char>,f2>>putchar(c>。//printf("\n">。fclose(f2>。}voidDecoding(>//译码:对文件CodeFile.dat中的报文进行译码,放在文件Textfile.txt中{FILE*f1,*f2。charc。ints,i,p。f1=fopen("CodeFile.dat","rb">。f2=fopen("Textfile.txt","w">。//printf("报文:">。p=2*char_num-1。//P指向哈夫曼树的根while(fread(&s,1,sizeof(int>,f1>>{//printf("%d",s>。if(s==0>p=HT[p].lchild。elsep=HT[p].rchild。if(HT[p].lchild==0>{c=HT[p].ch。fputc(c,f2>。p=2*char_num-1。//让P重新指向哈夫曼树的根}}printf("\n">。fclose(f1>。fclose(f2>。//f2=fopen("Textfile.txt","r">。//printf("原文:">。//while(fread(&c,1,sizeof(char>,f2>>putchar(c>。//printf("\n">。//fclose(f2>。}六、源程序见电子稿(文件名为:Huffman_Tree.cpp>。七、运行结果与运行情况第一次运行:创建数据文件DataFile.dat请输入一段仅包含大写字母A--H的一段文字以#结束ABCHHDHFGEHDBCHDGFA#字符及每个字符出现的次数(字符,出现次数>(A,2>,(B,2>,(C,2>,(D,3>,(E,1>,(F,2>,(G,2>,(H,5>初始的哈夫曼树:字符权值双亲左孩子右孩子指向编码的指针A2900->(null>B21000->(null>C21000->(null>D31200->(null>E1900->(null>F21100->(null>G21100->(null>H51400->(null>31215->(null>41323->(null>41367->(null>61449->(null>8151011->(null>1115812->(null>1901314->(null>编码后的哈夫曼树:字符权值双亲左孩子右孩子指向编码的指针A2900->1110B21000->000C21000->001D31200->110E1900->1111F21100->010G21100->011H51400->1031215->(null>41323->(null>41367->(null>61449->(null>8151011->(null>1115812->(null>1901314->(null>字符A的编码为:1110字符B的编码为:000字符C的编码为:001字符D的编码为:110字符E的编码为:1111字符F的编码为:010字符G的编码为:011字符H的编码为:10创建原文文件,请输入一段仅包含大写字母A--H的一段文字作为原文以#结束ABCGDBCHFE#原文:ABCGDBCHFE译文:111000*********0000001100101111创建报文文件,请输入一段仅包含0和1的一段文字作为原文以2作为结束标志.输入报文中的第一个代码0或1<为整数类型>000011100101020000111001010报文:0000111001010原文:BGHFH第二次运行创建数据文件DataFile.dat请输入一段仅包含大写字母A--H的一段文字以#结束AABABABABABAEGHF#字符及每个字符出现的次数(字符,出现次数>(A,7>,(B,5>,(C,0>,(D,0>,(E,1>,(F,1>,(G,1>,(H,1>,初始的哈夫曼树:字符权值双亲左孩子右孩子指向编码的指针A71500->(null>B51400->(null>C0900->(null>D0900->(null>E11000->(null>F11100->(null>G11100->(null>H11200->(null>01034->(null>11259->(null>21367->(null>213810->(null>4141112->(null>915213->(null>160114->(null>编码后的哈夫曼树:字符权值双亲左孩子右孩子指向编码的指针A71500->0B51400->10C0900->111110D0900->111111E11000->11110F11100->1100G11100->1101H11200->111001034->(null>11259->(null>21367->(null>213810->(null>4141112->(null>915213->(null>160114->(null>字符A的编码为:0字符B的编码为:10字符C的编码为:111110字符D的编码为:111111字符E的编码为:11110字符F的编码为:1100字符G的编码为:1101字符H的编码为:1110创建原文文件,请输入一段仅包含大写字母A--H的一段文字作为原文以#结束ABCBDGFBCGD#原文:ABCBDGFBCGD:010*********创建报文文件,请输入一段仅包含0和1的一段文字作为原文以2作为结束标志.输入报文中的第一个代码0或1<为整数类型>000101010100020001010101000报文:0001010101000原文:AAABBBBAAPressanykeytocontinue八、自我评析与总结该实验做得比较成功,例如在编写调试程序时,并不是仅仅把目标局限在编译通过,执行有结果上。除了为了完成任务的模块外,另有测试输出哈夫曼树的代码print_huff_tree(>,输出文件内容的代码;在适当的地方调用它们,运行时可以看到验证编写程序的正确性;当运行通过并符合题中要求后将这一部分代码变成注释或删除。通过本次实验,提高了自已调试程序的能力。充分体会到了在程序执行时的提示性输出的重要性。编写大一点的程序,应先写出算法,再写程序,调试时一段一段调试;对于没有实现的操作用空操作代替,这样容易找出错误所在。最忌讳将所有代码写完后再调试,这样若程序有错误,太难找。需要特别强调的是:编写程序、调试程序不要仅局限于编译通过上,关键要出正确的结果,在必要的时候添加提示输出信息,是一种不错的选择。该程序具有通用性,对于不同的输入都可进行处理。若想对同一个编码方案进行编译码,只需在第一次运行时创建数据文件,当数据文件创建好后,仅需将源程序的主函数中调用创建数据文件函数creatDataFile(>的那个语句变为注释既可。九、参考文献1.蒋盛益等编著《数据结构学习指导与训练》中国水利水电出版社2.严蔚敏吴伟民编著《数据结构》<C语言版)清华大学出版社3.严蔚敏等编著《数据结构习题集》<C语言版)清华大学出版社十、教师评语:
从数据文件DataFile.dat中读入字符及每个字符的权值,建立哈夫曼树HuffTree;
2)编码用已建好的哈夫曼树,对文件ToBeTran.dat中的文本进行编码形成报文,将报文写在文件Code.txt中;3)译码利用已建好的哈夫曼树,对文件CodeFile.dat中的代码进行译码形成原文,结果存入文件Textfile.txt中;4)输出输出ToBeTran.dat及其报文Code.txt;输出CodeFile.dat及其原文Textfile.txt;[备注]:1)数据文件DataFile.dat中,元素类型为<字符,权值),DataFile.dat的建立可以根据用户输入的一段原文,通过统计出现的字符及各字符出现的次数<与出现的概率作用相同)而得到。另:为了使输出的哈夫曼树不太大,规定报文中只能出现A-H的8个字符,当然对程序稍加修改就可以对出现的所有可输和字符进行处理。2)ToBeTran.dat中的内容由用户在程序执行时从键盘随机输入得到;3)CodeFile.dat中的内容由用户在程序执行时从键盘随机输入得到;DataFile.dat、ToBeTran.dat、CodeFile.dat在程序运行时建立是为了保证编/译码系统的通用性;三.总的设计思想,及环境语言、工具等总的设计思想:1.根据实验要求,先创建文件DataFile.dat、ToBeTran.dat和CodeFile.dat;用下面三个函数实现;voidcreatDataFile(>;//创建数据文件voidcreatToBeTran(>;//创建原文文件voidcreatCodeFile(>;//创建报文文件2.从文件DataFile.dat中读出各字符及相应的权值,创建哈夫曼树HT;3.根据构造的哈夫曼树,求相应字符的哈夫曼编码;4.从文件ToBeTran.dat中读出要翻译的原文,将其翻译成译文存入文件Code.txt中5.从文件CodeFile.dat中读出要翻译的报文,将其翻译成原文存入文件Textfile.txt中环境语言:Windows下的MicrosoftVC++四、数据结构与模块说明下面是编译码系统中所用的数据结构。在这个系统中,哈夫曼树的存储结构采用顺序存储;其结点结构为:程序中用到的头文件、类型定义及主要的函数原型如下:#include"stdio.h"#include"malloc.h"#include"string.h"#definechar_num8typedefstruct{charch。intw。}DFileNode。//定义数据文件的元素类型typedefstruct//赫夫曼树和赫夫曼编码的存储表示{charch。//相应字符intweight。//字符的权值intparent,lchild,rchild。//双亲、左、右孩子指针<下标)char*next。//指向该字符的编码的指针}HTNode,*HuffmanTree。//动态分配数组存储赫夫曼树HuffmanTreeHT。voidcreatDataFile(>//创建数据文件voidcreatToBeTran(>//创建原文文件voidcreatCodeFile(>//创建报文文件intmin(HuffmanTreet,inti>;//求无双亲且权值最小的结点,函数voidselect(>调用voidselect(HuffmanTreet,inti,int&s1,int&s2>;//s1为最小的两个值中序号小的那个voidprint_huff_tree(HuffmanTreeHT,intn>;//输出哈夫曼树,在必要时调用以验证算法的正确性voidcreatHuffmanTree(HuffmanTree&HT,intn>;//创建含n叶子结点的哈夫曼树,字符及权值在文件DataFile.dat中即初始化voidHuffmanCoding(HuffmanTree&HT,intn>;//对有n个叶子结点的哈夫曼树上的叶子结点进行编码voidEnCoding(>//编码:对文件ToBeTran.dat中的文本进行编码,放在文件Code.txt中voidDecoding(>;//译码:对文件CodeFile.dat中的报文进行译码,放在文件Textfile.txt中voidoutput(>;//输出原文和对应的译文;输出报文和对应的原文;五、主要算法的设计与实现voidcreatHuffmanTree(HuffmanTree&HT,intn>{//创建含n叶子结点的哈夫曼树,字符及权值在文件DataFile.dat中即初始化InitialzationFILE*f1。intm,i,s1,s2。HuffmanTreep。DFileNodes。//从文件中读数据时用;m=2*n-1。f1=fopen("DataFile.dat","rb">。HT=(HuffmanTree>malloc((m+1>*sizeof(HTNode>>。//0号单元未用printf("字符及相应的权值为:">。for(p=HT+1,i=1。i<=n。++i,++p>{fread(&s,1,sizeof(DFileNode>,f1>。//从文件中读一个数给s,构造叶子printf("(%c,%d>",s.ch,s.w>。(*p>.ch=s.ch。(*p>.weight=s.w。(*p>.parent=0。(*p>.lchild=0。(*p>.rchild=0。(*p>.next=NULL。}fclose(f1>。printf("\n">。for(。i<=m。++i,++p>{(*p>.ch=''。(*p>.parent=0。(*p>.lchild=0。(*p>.rchild=0。(*p>.next=NULL。}for(i=n+1。i<=m。++i>//建n-1个分支结点{//在HT[1~i-1]中选择parent为0且weight最小的两个结点,其序号分别为s1和s2select(HT,i-1,s1,s2>。HT[s1].parent=HT[s2].parent=i。HT[i].lchild=s1。HT[i].rchild=s2。HT[i].weight=HT[s1].weight+HT[s2].weight。}}voidHuffmanCoding(HuffmanTree&HT,intn>//对有n个叶子结点的哈夫曼树上的叶子结点进行编码{char*cd。inti,start,c,f。cd=(char*>malloc(n*sizeof(char>>。//分配求编码的工作空间cd[n-1]='\0'。//编码结束符for(i=1。i<=n。i++>{//逐个字符求赫夫曼编码start=n-1。//编码结束符位置for(c=i,f=HT[i].parent。f!=0。c=f,f=HT[f].parent>//从叶子到根逆向求编码if(HT[f].lchild==c>cd[--start]='0'。elsecd[--start]='1'。HT[i].next=(char*>malloc((n-start>*sizeof(char>>。//为第i个字符编码分配空间strcpy(HT[i].next,&cd[start]>。//从cd复制编码(串>到HC}free(cd>。//释放工作空间}voidEnCoding(>//编码:对文件ToBeTran.dat中的文本进行编码,放在文件Code.txt中{FILE*f1,*f2。charc。ints,i。f1=fopen("ToBeTran.dat","r">。//打开文件ToBeTran.dat为了读f2=fopen("Code.txt","w">。//打开文件Code.txt为了写//printf("原文:">。while(fread(&c,1,sizeof(char>,f1>>{i=1。while(HT[i].ch!=c>i++。//printf("%c",c>。fputs(HT[i].next,f2>。}//printf("\n">。fclose(f1>。fclose(f2>。//f2=fopen("Code.txt","r">。//printf("译文:">。//while(fread(&c,1,sizeof(char>,f2>>putchar(c>。//printf("\n">。fclose(f2>。}voidDecoding(>//译码:对文件CodeFile.dat中的报文进行译码,放在文件Textfile.txt中{FILE*f1,*f2。charc。ints,i,p。f1=fopen("CodeFile.dat","rb">。f2=fopen("Textfile.txt","w">。//printf("报文:">。p=2*char_num-1。//P指向哈夫曼树的根while(fread(&s,1,sizeof(int>,f1>>{//printf("%d",s>。if(s==0>p=HT[p].lchild。elsep=HT[p].rchild。if(HT[p].lchild==0>{c=HT[p].ch。fputc(c,f2>。p=2*char_num-1。//让P重新指向哈夫曼树的根}}printf("\n">。fclose(f1>。fclose(f2>。//f2=fopen("Textfile.txt","r">。//printf("原文:">。//while(fread(&c,1,sizeof(char>,f2>>putchar(c>。//printf("\n">。//fclose(f2>。}六、源程序见电子稿(文件名为:Huffman_Tree.cpp>。七、运行结果与运行情况第一次运行:创建数据文件DataFile.dat请输入一段仅包含大写字母A--H的一段文字以#结束ABCHHDHFGEHDBCHDGFA#字符及每个字符出现的次数(字符,出现次数>(A,2>,(B,2>,(C,2>,(D,3>,(E,1>,(F,2>,(G,2>,(H,5>初始的哈夫曼树:字符权值双亲左孩子右孩子指向编码的指针A2900->(null>B21000->(null>C21000->(null>D31200->(null>E1900->(null>F21100->(null>G21100->(null>H51400->(null>31215->(null>41323->(null>41367->(null>61449->(null>8151011->(null>1115812->(null>1901314->(null>编码后的哈夫曼树:字符权值双亲左孩子右孩子指向编码的指针A2900->1110B21000->000C21000->001D31200->110E1900->1111F21100->010G21100->011H51400->1031215->(null>41323->(null>41367->(null>61449->(null>8151011->(null>1115812->(null>1901314->(null>字符A的编码为:1110字符B的编码为:000字符C的编码为:001字符D的编码为:110字符E的编码为:1111字符F的编码为:010字符G的编码为:011字符H的编码为:10创建原文文件,请输入一段仅包含大写字母A--H的一段文字作为原文以#结束ABCGDBCHFE#原文:ABCGDBCHFE译文:111000*********0000001100101111创建报文文件,请输入一段仅包含0和1的一段文字作为原文以2作为结束标志.输入报文中的第一个代码0或1<为整数类型>000011100101020000111001010报文:0000111001010原文:BGHFH第二次运行创建数据文件DataFile.dat请输入一段仅包含大写字母A--H的一段文字以#结束AABABABABABAEGHF#字符及每个字符出现的次数(字符,出现次数>(A,7>,(B,5>,(C,0>,(D,0>,(E,1>,(F,1>,(G,1>,(H,1>,初始的哈夫曼树:字符权值双亲左孩子右孩子指向编码的指针A71500->(null>B51400->(null>C0900->(null>D0900->(null>E11000->(null>F11100->(null>G11100->(null>H11200->(null>01034->(null>11259->(null>21367->(null>213810->(null>4141112->(null>915213->(null>160114->(null>编码后的哈夫曼树:字符权值双亲左孩子右孩子指向编码的指针A71500->0B51400->10C0900->111110D0900->111111E11000->11110F11100->1100G11100->1101H11200->111001034->(null>11259->(null>21367->(null>213810->(null>4141112->(null>915213->(null>160114->(null>字符A的编码为:0字符B的编码为:10字符C的编码为:111110字符D的编码为:111111字符E的编码为:11110字符F的编码为:1100字符G的编码为:1101字符H的编码为:1110创建原文文件,请输入一段仅包含大写字母A--H的一段文字作为原文以#结束ABCBDGFBCGD#原文:ABCBDGFBCGD:010*********创建报文文件,请输入一段仅包含0和1的一段文字作为原文以2作为结束标志.输入报文中的第一个代码0或1<为整数类型>000101010100020001010101000报文:0001010101000原文:AAABBBBAAPressanykeytocontinue八、自我评析与总结该实验做得比较成功,例如在编写调试程序时,并不是仅仅把目标局限在编译通过,执行有结果上。除了为了完成任务的模块外,另有测试输出哈夫曼树的代码print_huff_tree(>,输出文件内容的代码;在适当的地方调用它们,运行时可以看到验证编写程序的正确性;当运行通过并符合题中要求后将这一部分代码变成注释或删除。通过本次实验,提高了自已调试程序的能力。充分体会到了在程序执行时的提示性输出的重要性。编写大一点的程序,应先写出算法,再写程序,调试时一段一段调试;对于没有实现的操作用空操作代替,这样容易找出错误所在。最忌讳将所有代码写完后再调试,这样若程序有错误,太难找。需要特别强调的是:编写程序、调试程序不要仅局限于编译通过上,关键要出正确的结果,在必要的时候添加提示输出信息,是一种不错的选择。该程序具有通用性,对于不同的输入都可进行处理。若想对同一个编码方案进行编译码,只需在第一次运行时创建数据文件,当数据文件创建好后,仅需将源程序的主函数中调用创建数据文件函数creatDataFile(>的那个语句变为注释既可。九、参考文献1.蒋盛益等编著《数据结构学习指导与训练》中国水利水电出版社2.严蔚敏吴伟民编著《数据结构》<C语言版)清华大学出版社3.严蔚敏等编著《数据结构习题集》<C语言版)清华大学出版社十、教师评语:
用已建好的哈夫曼树,对文件ToBeTran.dat中的文本进行编码形成报文,将报文写在文件Code.txt中;
3)译码利用已建好的哈夫曼树,对文件CodeFile.dat中的代码进行译码形成原文,结果存入文件Textfile.txt中;4)输出输出ToBeTran.dat及其报文Code.txt;输出CodeFile.dat及其原文Textfile.txt;[备注]:1)数据文件DataFile.dat中,元素类型为<字符,权值),DataFile.dat的建立可以根据用户输入的一段原文,通过统计出现的字符及各字符出现的次数<与出现的概率作用相同)而得到。另:为了使输出的哈夫曼树不太大,规定报文中只能出现A-H的8个字符,当然对程序稍加修改就可以对出现的所有可输和字符进行处理。2)ToBeTran.dat中的内容由用户在程序执行时从键盘随机输入得到;3)CodeFile.dat中的内容由用户在程序执行时从键盘随机输入得到;DataFile.dat、ToBeTran.dat、CodeFile.dat在程序运行时建立是为了保证编/译码系统的通用性;三.总的设计思想,及环境语言、工具等总的设计思想:1.根据实验要求,先创建文件DataFile.dat、ToBeTran.dat和CodeFile.dat;用下面三个函数实现;voidcreatDataFile(>;//创建数据文件voidcreatToBeTran(>;//创建原文文件voidcreatCodeFile(>;//创建报文文件2.从文件DataFile.dat中读出各字符及相应的权值,创建哈夫曼树HT;3.根据构造的哈夫曼树,求相应字符的哈夫曼编码;4.从文件ToBeTran.dat中读出要翻译的原文,将其翻译成译文存入文件Code.txt中5.从文件CodeFile.dat中读出要翻译的报文,将其翻译成原文存入文件Textfile.txt中环境语言:Windows下的MicrosoftVC++四、数据结构与模块说明下面是编译码系统中所用的数据结构。在这个系统中,哈夫曼树的存储结构采用顺序存储;其结点结构为:程序中用到的头文件、类型定义及主要的函数原型如下:#include"stdio.h"#include"malloc.h"#include"string.h"#definechar_num8typedefstruct{charch。intw。}DFileNode。//定义数据文件的元素类型typedefstruct//赫夫曼树和赫夫曼编码的存储表示{charch。//相应字符intweight。//字符的权值intparent,lchild,rchild。//双亲、左、右孩子指针<下标)char*next。//指向该字符的编码的指针}HTNode,*HuffmanTree。//动态分配数组存储赫夫曼树HuffmanTreeHT。voidcreatDataFile(>//创建数据文件voidcreatToBeTran(>//创建原文文件voidcreatCodeFile(>//创建报文文件intmin(HuffmanTreet,inti>;//求无双亲且权值最小的结点,函数voidselect(>调用voidselect(HuffmanTreet,inti,int&s1,int&s2>;//s1为最小的两个值中序号小的那个voidprint_huff_tree(HuffmanTreeHT,intn>;//输出哈夫曼树,在必要时调用以验证算法的正确性voidcreatHuffmanTree(HuffmanTree&HT,intn>;//创建含n叶子结点的哈夫曼树,字符及权值在文件DataFile.dat中即初始化voidHuffmanCoding(HuffmanTree&HT,intn>;//对有n个叶子结点的哈夫曼树上的叶子结点进行编码voidEnCoding(>//编码:对文件ToBeTran.dat中的文本进行编码,放在文件Code.txt中voidDecoding(>;//译码:对文件CodeFile.dat中的报文进行译码,放在文件Textfile.txt中voidoutput(>;//输出原文和对应的译文;输出报文和对应的原文;五、主要算法的设计与实现voidcreatHuffmanTree(HuffmanTree&HT,intn>{//创建含n叶子结点的哈夫曼树,字符及权值在文件DataFile.dat中即初始化InitialzationFILE*f1。intm,i,s1,s2。HuffmanTreep。DFileNodes。//从文件中读数据时用;m=2*n-1。f1=fopen("DataFile.dat","rb">。HT=(HuffmanTree>malloc((m+1>*sizeof(HTNode>>。//0号单元未用printf("字符及相应的权值为:">。for(p=HT+1,i=1。i<=n。++i,++p>{fread(&s,1,sizeof(DFileNode>,f1>。//从文件中读一个数给s,构造叶子printf("(%c,%d>",s.ch,s.w>。(*p>.ch=s.ch。(*p>.weight=s.w。(*p>.parent=0。(*p>.lchild=0。(*p>.rchild=0。(*p>.next=NULL。}fclose(f1>。printf("\n">。for(。i<=m。++i,++p>{(*p>.ch=''。(*p>.parent=0。(*p>.lchild=0。(*p>.rchild=0。(*p>.next=NULL。}for(i=n+1。i<=m。++i>//建n-1个分支结点{//在HT[1~i-1]中选择parent为0且weight最小的两个结点,其序号分别为s1和s2select(HT,i-1,s1,s2>。HT[s1].parent=HT[s2].parent=i。HT[i].lchild=s1。HT[i].rchild=s2。HT[i].weight=HT[s1].weight+HT[s2].weight。}}voidHuffmanCoding(HuffmanTree&HT,intn>//对有n个叶子结点的哈夫曼树上的叶子结点进行编码{char*cd。inti,start,c,f。cd=(char*>malloc(n*sizeof(char>>。//分配求编码的工作空间cd[n-1]='\0'。//编码结束符for(i=1。i<=n。i++>{//逐个字符求赫夫曼编码start=n-1。//编码结束符位置for(c=i,f=HT[i].parent。f!=0。c=f,f=HT[f].parent>//从叶子到根逆向求编码if(HT[f].lchild==c>cd[--start]='0'。elsecd[--start]='1'。HT[i].next=(char*>malloc((n-start>*sizeof(char>>。//为第i个字符编码分配空间strcpy(HT[i].next,&cd[start]>。//从cd复制编码(串>到HC}free(cd>。//释放工作空间}voidEnCoding(>//编码:对文件ToBeTran.dat中的文本进行编码,放在文件Code.txt中{FILE*f1,*f2。charc。ints,i。f1=fopen("ToBeTran.dat","r">。//打开文件ToBeTran.dat为了读f2=fopen("Code.txt","w">。//打开文件Code.txt为了写//printf("原文:">。while(fread(&c,1,sizeof(char>,f1>>{i=1。while(HT[i].ch!=c>i++。//printf("%c",c>。fputs(HT[i].next,f2>。}//printf("\n">。fclose(f1>。fclose(f2>。//f2=fopen("Code.txt","r">。//printf("译文:">。//while(fread(&c,1,sizeof(char>,f2>>putchar(c>。//printf("\n">。fclose(f2>。}voidDecoding(>//译码:对文件CodeFile.dat中的报文进行译码,放在文件Textfile.txt中{FILE*f1,*f2。charc。ints,i,p。f1=fopen("CodeFile.dat","rb">。f2=fopen("Textfile.txt","w">。//printf("报文:">。p=2*char_num-1。//P指向哈夫曼树的根while(fread(&s,1,sizeof(int>,f1>>{//printf("%d",s>。if(s==0>p=HT[p].lchild。elsep=HT[p].rchild。if(HT[p].lchild==0>{c=HT[p].ch。fputc(c,f2>。p=2*char_num-1。//让P重新指向哈夫曼树的根}}printf("\n">。fclose(f1>。fclose(f2>。//f2=fopen("Textfile.txt","r">。//printf("原文:">。//while(fread(&c,1,sizeof(char>,f2>>putchar(c>。//printf("\n">。//fclose(f2>。}六、源程序见电子稿(文件名为:Huffman_Tree.cpp>。七、运行结果与运行情况第一次运行:创建数据文件DataFile.dat请输入一段仅包含大写字母A--H的一段文字以#结束ABCHHDHFGEHDBCHDGFA#字符及每个字符出现的次数(字符,出现次数>(A,2>,(B,2>,(C,2>,(D,3>,(E,1>,(F,2>,(G,2>,(H,5>初始的哈夫曼树:字符权值双亲左孩子右孩子指向编码的指针A2900->(null>B21000->(null>C21000->(null>D31200->(null>E1900->(null>F21100->(null>G21100->(null>H51400->(null>31215->(null>41323->(null>41367->(null>61449->(null>8151011->(null>1115812->(null>1901314->(null>编码后的哈夫曼树:字符权值双亲左孩子右孩子指向编码的指针A2900->1110B21000->000C21000->001D31200->110E1900->1111F21100->010G21100->011H51400->1031215->(null>41323->(null>41367->(null>61449->(null>8151011->(null>1115812->(null>1901314->(null>字符A的编码为:1110字符B的编码为:000字符C的编码为:001字符D的编码为:110字符E的编码为:1111字符F的编码为:010字符G的编码为:011字符H的编码为:10创建原文文件,请输入一段仅包含大写字母A--H的一段文字作为原文以#结束ABCGDBCHFE#原文:ABCGDBCHFE译文:111000*********0000001100101111创建报文文件,请输入一段仅包含0和1的一段文字作为原文以2作为结束标志.输入报文中的第一个代码0或1<为整数类型>000011100101020000111001010报文:0000111001010原文:BGHFH第二次运行创建数据文件DataFile.dat请输入一段仅包含大写字母A--H的一段文字以#结束AABABABABABAEGHF#字符及每个字符出现的次数(字符,出现次数>(A,7>,(B,5>,(C,0>,(D,0>,(E,1>,(F,1>,(G,1>,(H,1>,初始的哈夫曼树:字符权值双亲左孩子右孩子指向编码的指针A71500->(null>B51400->(null>C0900->(null>D0900->(null>E11000->(null>F11100->(null>G11100->(null>H11200->(null>01034->(null>11259->(null>21367->(null>213810->(null>4141112->(null>915213->(null>160114->(null>编码后的哈夫曼树:字符权值双亲左孩子右孩子指向编码的指针A71500->0B51400->10C0900->111110D0900->111111E11000->11110F11100->1100G11100->1101H11200->111001034->(null>11259->(null>21367->(null>213810->(null>4141112->(null>915213->(null>160114->(null>字符A的编码为:0字符B的编码为:10字符C的编码为:111110字符D的编码为:111111字符E的编码为:11110字符F的编码为:1100字符G的编码为:1101字符H的编码为:1110创建原文文件,请输入一段仅包含大写字母A--H的一段文字作为原文以#结束ABCBDGFBCGD#原文:ABCBDGFBCGD:010*********创建报文文件,请输入一段仅包含0和1的一段文字作为原文以2作为结束标志.输入报文中的第一个代码0或1<为整数类型>000101010100020001010101000报文:0001010101000原文:AAABBBBAAPressanykeytocontinue八、自我评析与总结该实验做得比较成功,例如在编写调试程序时,并不是仅仅把目标局限在编译通过,执行有结果上。除了为了完成任务的模块外,另有测试输出哈夫曼树的代码print_huff_tree(>,输出文件内容的代码;在适当的地方调用它们,运行时可以看到验证编写程序的正确性;当运行通过并符合题中要求后将这一部分代码变成注释或删除。通过本次实验,提高了自已调试程序的能力。充分体会到了在程序执行时的提示性输出的重要性。编写大一点的程序,应先写出算法,再写程序,调试时一段一段调试;对于没有实现的操作用空操作代替,这样容易找出错误所在。最忌讳将所有代码写完后再调试,这样若程序有错误,太难找。需要特别强调的是:编写程序、调试程序不要仅局限于编译通过上,关键要出正确的结果,在必要的时候添加提示输出信息,是一种不错的选择。该程序具有通用性,对于不同的输入都可进行处理。若想对同一个编码方案进行编译码,只需在第一次运行时创建数据文件,当数据文件创建好后,仅需将源程序的主函数中调用创建数据文件函数creatDataFile(>的那个语句变为注释既可。九、参考文献1.蒋盛益等编著《数据结构学习指导与训练》中国水利水电出版社2.严蔚敏吴伟民编著《数据结构》<C语言版)清华大学出版社3.严蔚敏等编著《数据结构习题集》<C语言版)清华大学出版社十、教师评语:
利用已建好的哈夫曼树,对文件CodeFile.dat中的代码进行译码形成原文,结果存入文件Textfile.txt中;
4)输出输出ToBeTran.dat及其报文Code.txt;输出CodeFile.dat及其原文Textfile.txt;[备注]:1)数据文件DataFile.dat中,元素类型为<字符,权值),DataFile.dat的建立可以根据用户输入的一段原文,通过统计出现的字符及各字符出现的次数<与出现的概率作用相同)而得到。另:为了使输出的哈夫曼树不太大,规定报文中只能出现A-H的8个字符,当然对程序稍加修改就可以对出现的所有可输和字符进行处理。2)ToBeTran.dat中的内容由用户在程序执行时从键盘随机输入得到;3)CodeFile.dat中的内容由用户在程序执行时从键盘随机输入得到;DataFile.dat、ToBeTran.dat、CodeFile.dat在程序运行时建立是为了保证编/译码系统的通用性;三.总的设计思想,及环境语言、工具等总的设计思想:1.根据实验要求,先创建文件DataFile.dat、ToBeTran.dat和CodeFile.dat;用下面三个函数实现;voidcreatDataFile(>;//创建数据文件voidcreatToBeTran(>;//创建原文文件voidcreatCodeFile(>;//创建报文文件2.从文件DataFile.dat中读出各字符及相应的权值,创建哈夫曼树HT;3.根据构造的哈夫曼树,求相应字符的哈夫曼编码;4.从文件ToBeTran.dat中读出要翻译的原文,将其翻译成译文存入文件Code.txt中5.从文件CodeFile.dat中读出要翻译的报文,将其翻译成原文存入文件Textfile.txt中环境语言:Windows下的MicrosoftVC++四、数据结构与模块说明下面是编译码系统中所用的数据结构。在这个系统中,哈夫曼树的存储结构采用顺序存储;其结点结构为:程序中用到的头文件、类型定义及主要的函数原型如下:#include"stdio.h"#include"malloc.h"#include"string.h"#definechar_num8typedefstruct{charch。intw。}DFileNode。//定义数据文件的元素类型typedefstruct//赫夫曼树和赫夫曼编码的存储表示{charch。//相应字符intweight。//字符的权值intparent,lchild,rchild。//双亲、左、右孩子指针<下标)char*next。//指向该字符的编码的指针}HTNode,*HuffmanTree。//动态分配数组存储赫夫曼树HuffmanTreeHT。voidcreatDataFile(>//创建数据文件voidcreatToBeTran(>//创建原文文件voidcreatCodeFile(>//创建报文文件intmin(HuffmanTreet,inti>;//求无双亲且权值最小的结点,函数voidselect(>调用voidselect(HuffmanTreet,inti,int&s1,int&s2>;//s1为最小的两个值中序号小的那个voidprint_huff_tree(HuffmanTreeHT,intn>;//输出哈夫曼树,在必要时调用以验证算法的正确性voidcreatHuffmanTree(HuffmanTree&HT,intn>;//创建含n叶子结点的哈夫曼树,字符及权值在文件DataFile.dat中即初始化voidHuffmanCoding(HuffmanTree&HT,intn>;//对有n个叶子结点的哈夫曼树上的叶子结点进行编码voidEnCoding(>//编码:对文件ToBeTran.dat中的文本进行编码,放在文件Code.txt中voidDecoding(>;//译码:对文件CodeFile.dat中的报文进行译码,放在文件Textfile.txt中voidoutput(>;//输出原文和对应的译文;输出报文和对应的原文;五、主要算法的设计与实现voidcreatHuffmanTree(HuffmanTree&HT,intn>{//创建含n叶子结点的哈夫曼树,字符及权值在文件DataFile.dat中即初始化InitialzationFILE*f1。intm,i,s1,s2。HuffmanTreep。DFileNodes。//从文件中读数据时用;m=2*n-1。f1=fopen("DataFile.dat","rb">。HT=(HuffmanTree>malloc((m+1>*sizeof(HTNode>>。//0号单元未用printf("字符及相应的权值为:">。for(p=HT+1,i=1。i<=n。++i,++p>{fread(&s,1,sizeof(DFileNode>,f1>。//从文件中读一个数给s,构造叶子printf("(%c,%d>",s.ch,s.w>。(*p>.ch=s.ch。(*p>.weight=s.w。(*p>.parent=0。(*p>.lchild=0。(*p>.rchild=0。(*p>.next=NULL。}fclose(f1>。printf("\n">。for(。i<=m。++i,++p>{(*p>.ch=''。(*p>.parent=0。(*p>.lchild=0。(*p>.rchild=0。(*p>.next=NULL。}for(i=n+1。i<=m。++i>//建n-1个分支结点{//在HT[1~i-1]中选择parent为0且weight最小的两个结点,其序号分别为s1和s2select(HT,i-1,s1,s2>。HT[s1].parent=HT[s2].parent=i。HT[i].lchild=s1。HT[i].rchild=s2。HT[i].weight=HT[s1].weight+HT[s2].weight。}}voidHuffmanCoding(HuffmanTree&HT,intn>//对有n个叶子结点的哈夫曼树上的叶子结点进行编码{char*cd。inti,start,c,f。cd=(char*>malloc(n*sizeof(char>>。//分配求编码的工作空间cd[n-1]='\0'。//编码结束符for(i=1。i<=n。i++>{//逐个字符求赫夫曼编码start=n-1。//编码结束符位置for(c=i,f=HT[i].parent。f!=0。c=f,f=HT[f].parent>//从叶子到根逆向求编码if(HT[f].lchild==c>cd[--start]='0'。elsecd[--start]='1'。HT[i].next=(char*>malloc((n-start>*sizeof(char>>。//为第i个字符编码分配空间strcpy(HT[i].next,&cd[start]>。//从cd复制编码(串>到HC}free(cd>。//释放工作空间}voidEnCoding(>//编码:对文件ToBeTran.dat中的文本进行编码,放在文件Code.txt中{FILE*f1,*f2。charc。ints,i。f1=fopen("ToBeTran.dat","r">。//打开文件ToBeTran.dat为了读f2=fopen("Code.txt","w">。//打开文件Code.txt为了写//printf("原文:">。while(fread(&c,1,sizeof(char>,f1>>{i=1。while(HT[i].ch!=c>i++。//printf("%c",c>。fputs(HT[i].next,f2>。}//printf("\n">。fclose(f1>。fclose(f2>。//f2=fopen("Code.txt","r">。//printf("译文:">。//while(fread(&c,1,sizeof(char>,f2>>putchar(c>。//printf("\n">。fclose(f2>。}voidDecoding(>//译码:对文件CodeFile.dat中的报文进行译码,放在文件Textfile.txt中{FILE*f1,*f2。charc。ints,i,p。f1=fopen("CodeFile.dat","rb">。f2=fopen("Textfile.txt","w">。//printf("报文:">。p=2*char_num-1。//P指向哈夫曼树的根while(fread(&s,1,sizeof(int>,f1>>{//printf("%d",s>。if(s==0>p=HT[p].lchild。elsep=HT[p].rchild。if(HT[p].lchild==0>{c=HT[p].ch。fputc(c,f2>。p=2*char_num-1。//让P重新指向哈夫曼树的根}}printf("\n">。fclose(f1>。fclose(f2>。//f2=fopen("Textfile.txt","r">。//printf("原文:">。//while(fread(&c,1,sizeof(char>,f2>>putchar(c>。//printf("\n">。//fclose(f2>。}六、源程序见电子稿(文件名为:Huffman_Tree.cpp>。七、运行结果与运行情况第一次运行:创建数据文件DataFile.dat请输入一段仅包含大写字母A--H的一段文字以#结束ABCHHDHFGEHDBCHDGFA#字符及每个字符出现的次数(字符,出现次数>(A,2>,(B,2>,(C,2>,(D,3>,(E,1>,(F,2>,(G,2>,(H,5>初始的哈夫曼树:字符权值双亲左孩子右孩子指向编码的指针A2900->(null>B21000->(null>C21000->(null>D31200->(null>E1900->(null>F21100->(null>G21100->(null>H51400->(null>31215->(null>41323->(null>41367->(null>61449->(null>8151011->(null>1115812->(null>1901314->(null>编码后的哈夫曼树:字符权值双亲左孩子右孩子指向编码的指针A2900->1110B21000->000C21000->001D31200->110E1900->1111F21100->010G21100->011H51400->1031215->(null>41323->(null>41367->(null>61449->(null>8151011->(null>1115812->(null>1901314->(null>字符A的编码为:1110字符B的编码为:000字符C的编码为:001字符D的编码为:110字符E的编码为:1111字符F的编码为:010字符G的编码为:011字符H的编码为:10创建原文文件,请输入一段仅包含大写字母A--H的一段文字作为原文以#结束ABCGDBCHFE#原文:ABCGDBCHFE译文:111000*********0000001100101111创建报文文件,请输入一段仅包含0和1的一段文字作为原文以2作为结束标志.输入报文中的第一个代码0或1<为整数类型>000011100101020000111001010报文:0000111001010原文:BGHFH第二次运行创建数据文件DataFile.dat请输入一段仅包含大写字母A--H的一段文字以#结束AABABABABABAEGHF#字符及每个字符出现的次数(字符,出现次数>(A,7>,(B,5>,(C,0>,(D,0>,(E,1>,(F,1>,(G,1>,(H,1>,初始的哈夫曼树:字符权值双亲左孩子右孩子指向编码的指针A71500->(null>B51400->(null>C0900->(null>D0900->(null>E11000->(null>F11100->(null>G11100->(null>H11200->(null>01034->(null>11259->(null>21367->(null>213810->(null>4141112->(null>915213->(null>160114->(null>编码后的哈夫曼树:字符权值双亲左孩子右孩子指向编码的指针A71500->0B51400->10C0900->111110D0900->111111E11000->11110F11100->1100G11100->1101H11200->111001034->(null>11259->(null>21367->(null>213810->(null>4141112->(null>915213->(null>160114->(null>字符A的编码为:0字符B的编码为:10字符C的编码为:111110字符D的编码为:111111字符E的编码为:11110字符F的编码为:1100字符G的编码为:1101字符H的编码为:1110创建原文文件,请输入一段仅包含大写字母A--H的一段文字作为原文以#结束ABCBDGFBCGD#原文:ABCBDGFBCGD:010*********创建报文文件,请输入一段仅包含0和1的一段文字作为原文以2作为结束标志.输入报文中的第一个代码0或1<为整数类型>000101010100020001010101000报文:0001010101000原文:AAABBBBAAPressanykeytocontinue八、自我评析与总结该实验做得比较成功,例如在编写调试程序时,并不是仅仅把目标局限在编译通过,执行有结果上。除了为了完成任务的模块外,另有测试输出哈夫曼树的代码print_huff_tree(>,输出文件内容的代码;在适当的地方调用它们,运行时可以看到验证编写程序的正确性;当运行通过并符合题中要求后将这一部分代码变成注释或删除。通过本次实验,提高了自已调试程序的能力。充分体会到了在程序执行时的提示性输出的重要性。编写大一点的程序,应先写出算法,再写程序,调试时一段一段调试;对于没有实现的操作用空操作代替,这样容易找出错误所在。最忌讳将所有代码写完后再调试,这样若程序有错误,太难找。需要特别强调的是:编写程序、调试程序不要仅局限于编译通过上,关键要出正确的结果,在必要的时候添加提示输出信息,是一种不错的选择。该程序具有通用性,对于不同的输入都可进行处理。若想对同一个编码方案进行编译码,只需在第一次运行时创建数据文件,当数据文件创建好后,仅需将源程序的主函数中调用创建数据文件函数creatDataFile(>的那个语句变为注释既可。九、参考文献1.蒋盛益等编著《数据结构学习指导与训练》中国水利水电出版社2.严蔚敏吴伟民编著《数据结构》<C语言版)清华大学出版社3.严蔚敏等编著《数据结构习题集》<C语言版)清华大学出版社十、教师评语:
输出ToBeTran.dat及其报文Code.txt;输出CodeFile.dat及其原文Textfile.txt;
[备注]:
1)数据文件DataFile.dat中,元素类型为<字符,权值),DataFile.dat的建立可以根据用户输入的一段原文,通过统计出现的字符及各字符出现的次数<与出现的概率作用相同)而得到。
另:
为了使输出的哈夫曼树不太大,规定报文中只能出现A-H的8个字符,当然对程序稍加修改就可以对出现的所有可输和字符进行处理。
2)ToBeTran.dat中的内容由用户在程序执行时从键盘随机输入得到;
3)CodeFile.dat中的内容由用户在程序执行时从键盘随机输入得到;
DataFile.dat、ToBeTran.dat、CodeFile.dat在程序运行时建立是为了保证编/译码系统的通用性;
三.总的设计思想,及环境语言、工具等
总的设计思想:
1.根据实验要求,先创建文件DataFile.dat、ToBeTran.dat和CodeFile.dat;用下面三个函数实现;
voidcreatDataFile(>;//创建数据文件
voidcreatToBeTran(>;//创建原文文件
voidcreatCodeFile(>;//创建报文文件
2.从文件DataFile.dat中读出各字符及相应的权值,创建哈夫曼树HT;
3.根据构造的哈夫曼树,求相应字符的哈夫曼编码;
4.从文件ToBeTran.dat中读出要翻译的原文,将其翻译成译文存入文件Code.txt中
5.从文件CodeFile.dat中读出要翻译的报文,将其翻译成原文存入文件Textfile.txt中
环境语言:
Windows下的MicrosoftVC++
四、数据结构与模块说明
下面是编译码系统中所用的数据结构。
在这个系统中,哈夫曼树的存储结构采用顺序存储;其结点结构为:
程序中用到的头文件、类型定义及主要的函数原型如下:
#include"stdio.h"
#include"malloc.h"
#include"string.h"
#definechar_num8
typedefstruct{charch。
intw。
}DFileNode。
//定义数据文件的元素类型
typedefstruct//赫夫曼树和赫夫曼编码的存储表示
{
charch。
//相应字符
intweight。
//字符的权值
intparent,lchild,rchild。
//双亲、左、右孩子指针<下标)
char*next。
//指向该字符的编码的指针
}HTNode,*HuffmanTree。
//动态分配数组存储赫夫曼树
HuffmanTreeHT。
voidcreatDataFile(>//创建数据文件
voidcreatToBeTran(>//创建原文文件
voidcreatCodeFile(>//创建报文文件
intmin(HuffmanTreet,inti>;//求无双亲且权值最小的结点,函数voidselect(>调用
voidselect(HuffmanTreet,inti,int&s1,int&s2>;
//s1为最小的两个值中序号小的那个
voidprint_huff_tree(HuffmanTreeHT,intn>;
//输出哈夫曼树,在必要时调用以验证算法的正确性
voidcreatHuffmanTree(HuffmanTree&HT,intn>;
//创建含n叶子结点的哈夫曼树,字符及权值在文件DataFile.dat中即初始化
voidHuffmanCoding(HuffmanTree&HT,intn>;
//对有n个叶子结点的哈夫曼树上的叶子结点进行编码
voidEnCoding(>//编码:
对文件ToBeTran.dat中的文本进行编码,放在文件Code.txt中
voidDecoding(>;//译码:
对文件CodeFile.dat中的报文进行译码,放在文件Textfile.txt中voidoutput(>;//输出原文和对应的译文;输出报文和对应的原文;
五、主要算法的设计与实现
voidcreatHuffmanTree(HuffmanTree&HT,intn>
{//创建含n叶子结点的哈夫曼树,字符及权值在文件DataFile.dat中即初始化Initialzation
FILE*f1。
intm,i,s1,s2。
HuffmanTreep。
DFileNodes。
//从文件中读数据时用;
m=2*n-1。
f1=fopen("DataFile.dat","rb">。
HT=(HuffmanTree>malloc((m+1>*sizeof(HTNode>>。
//0号单元未用
printf("字符及相应的权值为:
">。
for(p=HT+1,i=1。
i<=n。
++i,++p>
fread(&s,1,sizeof(DFileNode>,f1>。
//从文件中读一个数给s,构造叶子
printf("(%c,%d>",s.ch,s.w>。
(*p>.ch=s.ch。
(*p>.weight=s.w。
(*p>.parent=0。
(*p>.lchild=0。
(*p>.rchild=0。
(*p>.next=NULL。
}
fclose(f1>。
printf("\n">。
for(。
i<=m。
{(*p>.ch=''。
for(i=n+1。
++i>//建n-1个分支结点
{//在HT[1~i-1]中选择parent为0且weight最小的两个结点,其序号分别为s1和s2
select(HT,i-1,s1,s2>。
HT[s1].parent=HT[s2].parent=i。
HT[i].lchild=s1。
HT[i].rchild=s2。
HT[i].weight=HT[s1].weight+HT[s2].weight。
voidHuffmanCoding(HuffmanTree&HT,intn>
char*cd。
inti,start,c,f。
cd=(char*>malloc(n*sizeof(char>>。
//分配求编码的工作空间
cd[n-1]='\0'。
//编码结束符
for(i=1。
i++>
{//逐个字符求赫夫曼编码
start=n-1。
//编码结束符位置
for(c=i,f=HT[i].parent。
f!
=0。
c=f,f=HT[f].parent>
//从叶子到根逆向求编码
if(HT[f].lchild==c>
cd[--start]='0'。
else
cd[--start]='1'。
HT[i].next=(char*>malloc((n-start>*sizeof(char>>。
//为第i个字符编码分配空间
strcpy(HT[i].next,&cd[start]>。
//从cd复制编码(串>到HC
free(cd>。
//释放工作空间
FILE*f1,*f2。
charc。
ints,i。
f1=fopen("ToBeTran.dat","r">。
//打开文件ToBeTran.dat为了读
f2=fopen("Code.txt","w">。
//打开文件Code.txt为了写
//printf("原文:
while(fread(&c,1,sizeof(char>,f1>>
{i=1。
while(HT[i].ch!
=c>i++。
//printf("%c",c>。
fputs(HT[i].next,f2>。
//printf("\n">。
fclose(f2>。
//f2=fopen("Code.txt","r">。
//printf("译文:
//while(fread(&c,1,sizeof(char>,f2>>putchar(c>。
voidDecoding(>//译码:
对文件CodeFile.dat中的报文进行译码,放在文件Textfile.txt中
ints,i,p。
f1=fopen("CodeFile.dat","rb">。
f2=fopen("Textfile.txt","w">。
//printf("报文:
p=2*char_num-1。
//P指向哈夫曼树的根
while(fread(&s,1,sizeof(int>,f1>>
//printf("%d",s>。
if(s==0>p=HT[p].lchild。
elsep=HT[p].rchild。
if(HT[p].lchild==0>
{c=HT[p].ch。
fputc(c,f2>。
//让P重新指向哈夫曼树的根
//f2=fopen("Textfile.txt","r">。
//fclose(f2>。
六、源程序
见电子稿(文件名为:
Huffman_Tree.cpp>。
七、运行结果与运行情况
第一次运行:
创建数据文件DataFile.dat
请输入一段仅包含大写字母A--H的一段文字以#结束
ABCHHDHFGEHDBCHDGFA#
字符及每个字符出现的次数(字符,出现次数>
(A,2>,(B,2>,(C,2>,(D,3>,(E,1>,(F,2>,(G,2>,(H,5>
初始的哈夫曼树:
字符权值双亲左孩子右孩子指向编码的指针
A2900->(null>
B21000->(null>
C21000->(null>
D31200->(null>
E1900->(null>
F21100->(null>
G21100->(null>
H51400->(null>
31215->(null>
41323->(null>
41367->(null>
61449->(null>
8151011->(null>
1115812->(null>
1901314->(null>
编码后的哈夫曼树:
A2900->1110
B21000->000
C21000->001
D31200->110
E1900->1111
F21100->010
G21100->011
H51400->10
字符A的编码为:
1110字符B的编码为:
000字符C的编码为:
001
字符D的编码为:
110字符E的编码为:
1111字符F的编码为:
010
字符G的编码为:
011字符H的编码为:
10
创建原文文件,请输入一段仅包含大写字母A--H的一段文字作为原文以#结束
ABCGDBCHFE#
原文:
ABCGDBCHFE
译文:
111000*********0000001100101111
创建报文文件,请输入一段仅包含0和1的一段文字作为原文以2作为结束标志.
输入报文中的第一个代码0或1<为整数类型>
00001110010102
0000111001010
报文:
原文:
BGHFH
第二次运行
AABABABABABAEGHF#
(A,7>,(B,5>,(C,0>,(D,0>,(E,1>,(F,1>,(G,1>,(H,1>,
A71500->(null>
B51400->(null>
C0900->(null>
D0900->(null>
E11000->(null>
F11100->(null>
G11100->(null>
H11200->(null>
01034->(null>
11259->(null>
21367->(null>
213810->(null>
4141112->(null>
915213->(null>
160114->(null>
A71500->0
B51400->10
C0900->111110
D0900->111111
E11000->11110
F11100->1100
G11100->1101
H11200->1110
0字符B的编码为:
10字符C的编码为:
111110
111111字符E的编码为:
11110字符F的编码为:
1100
1101字符H的编码为:
1110
ABCBDGFBCGD#
ABCBDGFBCGD
:
010*********
00010101010002
0001010101000
AAABBBBAA
Pressanykeytocontinue
八、自我评析与总结
该实验做得比较成功,例如在编写调试程序时,并不是仅仅把目标局限在编译通过,执行有结果上。
除了为了完成任务的模块外,另有测试输出哈夫曼树的代码print_huff_tree(>,输出文件内容的代码;在适当的地方调用它们,运行时可以看到验证编写程序的正确性;当运行通过并符合题中要求后将这一部分代码变成注释或删除。
通过本次实验,提高了自已调试程序的能力。
充分体会到了在程序执行时的提示性输出的重要性。
编写大一点的程序,应先写出算法,再写程序,调试时一段一段调试;对于没有实现的操作用空操作代替,这样容易找出错误所在。
最忌讳将所有代码写完后再调试,这样若程序有错误,太难找。
需要特别强调的是:
编写程序、调试程序不要仅局限于编译通过上,关键要出正确的结果,在必要的时候添加提示输出信息,是一种不错的选择。
该程序具有通用性,对于不同的输入都可进行处理。
若想对同一个编码方案进行编译码,只需在第一次运行时创建数据文件,当数据文件创建好后,仅需将源程序的主函数中调用创建数据文件函数creatDataFile(>的那个语句变为注释既可。
九、参考文献
1.蒋盛益等编著《数据结构学习指导与训练》中国水利水电出版社
2.严蔚敏吴伟民编著《数据结构》<C语言版)清华大学出版社
3.严蔚敏等编著《数据结构习题集》<C语言版)清华大学出版社
十、教师评语:
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1