当前位置:文档之家› 数据结构课程设计哈夫曼编译码器

数据结构课程设计哈夫曼编译码器

《数据结构》课程设计设计题目:哈弗曼编/译码器专业:网络工程班级:24070901学号:2407090133姓名:王璇目录1. 问题描述……………………………………………第 2页2. 系统设计……………………………………………第 2页3. 数据结构与算法描述………………………………第 5页4. 测试结果与分析……………………………………第 6页5. 总结 (10)6. 参考文献 (10)附录程序源代码 (11)课程设计题目1. 问题描述利用哈夫曼编码进行信息通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。

但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。

试为这样的信息传输写一个哈夫曼编/译码系统。

2. 系统设计2.1 设计目标一个完整的系统应具有以下功能:1)I:初始化(Initialization)。

从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmTree中。

输出哈夫曼树,及各字符对应的编码。

2)W:输入(Input)。

从终端读入需要编码的字符串s,将字符串s存入文件Tobetran.txt中。

3)E:编码(Encoding)与译码(Decoding)。

编码(Encoding)。

利用已建好的哈夫曼树(如不在内存,则从文件htmTree中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。

译码(Decoding)。

利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件TextFile中。

印代码文件(Print)。

将文件CodeFile以紧凑格式显示在终端上,每行50个代码。

同时将此字符形式的编码写入文件CodePrint中。

4)T:印哈夫曼树(Tree Printing)。

将已在内存中的哈夫曼树以直观的方式(树或凹入表形式)显示在终端上,同时将此字符形式的哈夫曼树写入文件TreePrint中。

5)Q:退出程序。

返回WINDOWS界面。

2.2 设计思想哈夫曼编码(Huffman Coding)是一种编码方式,以哈夫曼树─即最优二叉树,带权路径长度最小的二叉树,经常应用于数据压缩。

是指使用一张特殊的编码表将源字符(例如某文件中的一个符号)进行编码。

这种方法是由David.A.Huffman发展起来的。

例如,在英文中,e 的出现概率很高,而z的出现概率则最低。

当利用哈夫曼编码对一篇英文进行压缩时,e极有可能用一个位(bit)来表示,而z则可能花去25个位(不是26)。

用普通的表示方法时,每个英文字母均占用一个字节(byte),即8个位。

二者相比,e使用了一般编码的1/8的长度,z 则使用了3倍多。

倘若我们能实现对于英文中各个字母出现概率的较准确的估算,就可以大幅度提高无损压缩的比例。

2.3 系统模块划分图2-3 哈夫曼编/解码器的程序结构图2.3.1 初始化算法:构造huffman tree的构造函数和析构函数2.3.2 编码算法:(1)对输入的一段欲编码的字符串进行统计各个字符出现的次数,并它们转化为权值{w1,w2,……,wN}构成n棵二叉树的集合F={T1,T2,……,Tn}把它们保存到结构体数组HT[n]中,其中{Ti是按它们的ASCⅡ码值先后排序。

其中每棵二叉树Ti中只有一个带权为Wi的根结点的权值为其左、右子树上根结点的权值之和。

(2)在HT[1..i]中选取两棵根结点的权值最小且没有被选过的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为左、右子树上根结点的权值之和。

(3)哈夫曼树已经建立后,从叶子到根逆向求每一个字符的哈夫曼编码。

2.3.3 译码算法:译码的过程是分解电文中字符串,从根出发,按字符'0',或'1'确定找左孩子或右孩子,直至叶子结点,便求的该子串相应字符并输出接着下一个字符。

3. 数据结构与算法描述3-1typedef struct{ int weight;int parent,lchild,rchild;}HTNode,* HuffmanTree; //动态分配数组存储赫夫曼树typedef char **HuffmanCode; //动态分配数组存储赫夫曼编码表3-2 int min(HuffmanTree t,int i) // ---------求赫夫曼编码-------------3-3 void select(HuffmanTree t,int i,int &s1,int &s2) //----slect函数----3-4void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int *w,int n)// w存放n个字符的权值(均>0),构造赫夫曼树HT,并求出n个字符的赫夫曼编码HC3-5 void Initialization() //----------初始化赫夫曼链表--------------3-6 void InputCode() //---------获取报文-------------3-7 void Encoding() //----------------编码函数------------------3-8 void Decoding() //-----------------译码函数-----------------3-9 void Code_printing() //-------------打印编码的函数-------------3-19 void coprint(HuffmanTree start,HuffmanTree HT)//------------------------打印赫夫曼树的函数-----------------------3-20 void main() //--------------------主函数-------------------4. 测试结果与分析A 186B 64C 13D 22E 32F 103G 21 H 15 I 47 J 57 K 15 L 32M 20 N 57 O 63 P 15 Q 1 R 48S 51 T 80 U 23 V 8 W 18 X 1Y 16 Z 1表4-1 abc.txt文件中的字母和权值声明:程序预先将Huffman编码解码所需的26个字母和权值保存在根目录下的abc.txt 文件下。

4-1.按照程序提示输入i对Huffman进行初始化。

4-2.初始化后程序对abc.txt文件中的数据进行读取并运行编码函数进行哈夫曼编码。

然后将字母、权值和哈夫曼编码存在根目录下的htmTree.txt文件中。

在屏幕显示出字符、权值、编码。

4-3.输入w进入待编码字符输入窗口,并键入字符串(注意单词间无空格)“happynewyear”。

4-4.可以看出所获得的字符串已经存入根目录下的tobetran.txt文件中。

4-5.输入e进行编码、译码和打印编码功能。

4-6.输入t打印哈夫曼树。

由于哈夫曼树过于巨大,一次截屏无法完全显示,使用两次截屏。

以上两幅图显示出来程序编出的哈夫曼树的形状。

打印出来的图形与教科书上的常见哈夫曼树略有不同,左边的数是右边数的父节点。

4-7.输入q退出程序。

5. 总结5-1、用户界面设计为“菜单”模式,使人们更加容易使用。

5-2、在程序的一次执行过程中,第一次执行e命令之后,哈夫曼树已经在内存了,不必再读入。

5-3.在编程中使用了很不规范的编程方法,应用了一些临时变量来实现功能,,而大量临时变量在代码中没有很好地进行命名。

这给程序的阅读和维护带来了极大的困难。

5-4.本程序仅能对26个小写字母构成的字符串进行处理,并不具有对汉字等的编码处理能力。

5-5.设计中得到了老师和广大同学的帮助,并参考了网络上的优秀论文和纸质文件,使我的程序设计能够较为顺利的进行下去。

在此我衷心感谢我的老师同学和对以上资源的作者。

6. 参考文献A:书籍资料[1] 李春葆《数据结构教程上机实验指导》北京:清华大学出版社[2] 严蔚敏吴伟民《数据结构(C语言版)》北京:清华大学出版社[3] 苏仕华《数据结构课程设计》北京:机械工业出版社B:网络资料[1] 哈夫曼编/译码器(课程设计).html[2]哈夫曼编码432169091693efce3bc763ab.html附录程序源代码//哈夫曼编/译码器(课程设计) 2008/5/21#include <iostream.h>#include <fstream.h>#include <iomanip.h>#include <string.h>#include <malloc.h>#include <stdio.h>#include <iomanip.h>const int UINT_MAX=10000;typedef struct{int weight;int parent,lchild,rchild;}HTNode,* HuffmanTree; //动态分配数组存储赫夫曼树typedef char **HuffmanCode; //动态分配数组存储赫夫曼编码表//--------------------全局变量-----------------------HuffmanTree HT;HuffmanCode HC;int *w,i,j;const int n=26;char *z;int flag=0;int numb=0;// -----------------求赫夫曼编码---------------------int min(HuffmanTree t,int i){ // 此函数将要被void select()调用int j,flag;int k=UINT_MAX; // 取k为不小于可能的值for(j=1;j<=i;j++)if(t[j].weight<k&&t[j].parent==0)k=t[j].weight,flag=j;t[flag].parent=1;return flag;}//--------------------slect函数----------------------void select(HuffmanTree t,int i,int &s1,int &s2){ // s1为最小的两个值中序号小的那个int j;s1=min(t,i);s2=min(t,i);if(s1>s2){j=s1;s1=s2;s2=j;}}// -------------------参考课本算法6.12-------------------void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int *w,int n){ // w存放n个字符的权值(均>0),构造赫夫曼树HT,并求出n个字符的赫夫曼编码HC int m,i,s1,s2,start;int c,f;HuffmanTree p;char *cd;if(n<=1)return;//检测结点数是否可以构成树m=2*n-1;HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode)); // 0号单元未用for(p=HT+1,i=1;i<=n;++i,++p,++w){p->weight=*w;p->parent=0;p->lchild=0;p->rchild=0;}for(;i<=m;++i,++p)p->parent=0;for(i=n+1;i<=m;++i) // 建赫夫曼树{ //在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;}// 从叶子到根逆向求每个字符的赫夫曼编码HC=(HuffmanCode)malloc((n+1)*sizeof(char*));// 分配n个字符编码的头指针向量([0]不用)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';HC[i]=(char*)malloc((n-start)*sizeof(char));// 为第i个字符编码分配空间strcpy(HC[i],&cd[start]); // 从cd复制编码(串)到HC}free(cd); // 释放工作空间}//----------------------初始化赫夫曼链表-------------------------void Initialization(){flag=1;int num2;cout<<"下面初始化赫夫曼链表"<<endl;w=(int*)malloc(n*sizeof(int)); // 为第26个字符权值分配空间z=(char*)malloc(n*sizeof(char)); // 为第26个字符分配空间cout<<"\n依次显示"<<n<<"个字符与其权值和编码\n"<<endl;char base[2];//?ifstream fin("abc.txt");for(i=0;i<n;i++){fin>>base;*(z+i)=*base;//?fin>>num2;//上面123行*(w+i)=num2;}HuffmanCoding(HT,HC,w,n);//----------------------------------打印编码---------------------------------------cout<<"字符"<<setw(6)<<"权值"<<setw(11)<<"编码"<<endl;for(i=1;i<=n;i++){cout<<setw(3)<<*(z+i-1);cout<<setw(6)<<*(w+i-1)<<setw(12)<<HC[i]<<endl;}//--------------------------将赫夫曼编码写入文件----------------------------cout<<"下面将赫夫曼编码写入文件"<<endl<<"...................."<<endl;FILE *htmTree;char r[]={' ','\0'};if((htmTree=fopen("htmTree.txt","w"))==NULL){cout<<"不能打开文件 "<<endl;return;}for(i=0;i<n;i++){fputc(*(z+i),htmTree);fputs(r,htmTree);}for(i=0;i<n;i++){fprintf(htmTree,"%6d",*(w+i));fputs(r,htmTree);}for(i=1;i<=n;i++){fputs(HC[i],htmTree);fputs(r,htmTree);}fclose(htmTree);cout<<"已将字符与对应编码写入根目录下文件htmTree.txt中"<<endl<<endl;}//--------------------------获取报文并写入文件---------------------------void InputCode(){FILE *tobetran;char str[100];if((tobetran=fopen("tobetran.txt","w"))==NULL){cout<<"不能打开文件"<<endl;return;}cout<<"请输入你想要编码的字符"<<endl; //字符个数应当小于100gets(str);fputs(str,tobetran);cout<<"获取报文成功"<<endl;fclose(tobetran);cout<<"...................."<<endl<"报文存入根目录下的tobetran.txt文件中"<<endl; }//---------------------------------编码函数---------------------------------void Encoding(){cout<<"下面对目录下文件tobetran.txt中的字符进行编码"<<endl;FILE *tobetran,*codefile;if((tobetran=fopen("tobetran.txt","rb"))==NULL){cout<<"不能打开文件"<<endl;}if((code("code","wb"))==NULL){cout<<"不能打开文件"<<endl;}char *tran;i=99;tran=(char*)malloc(100*sizeof(char));while(i==99){if(fgets(tran,100,tobetran)==NULL){cout<<"不能打开文件"<<endl;break;}for(i=0;*(tran+i)!='\0';i++){for(j=0;j<=n;j++){if(*(z+j-1)==*(tran+i)){fputs(HC[j],codefile);if(j>n){cout<<"字符错误,无法编码!"<<endl;break;}}}}}cout<<"…………编码完成…………"<<endl;cout<<"编码写入目录下的code中"<<endl<<endl;fclose(tobetran);fclose(codefile);free(tran);}//-------------------------译码函数---------------------------void Decoding(){cout<<"下面对根目录下文件code中的字符进行译码"<<endl; FILE *codef,*txtfile;if((txt("\\Text","w"))==NULL){cout<<"不能打开文件"<<endl;}txt("Text","w");if ((codef=fopen("code","r"))==NULL){cout<<"不能打开文件"<<endl;}codef=fopen("code","r");char *work,*work2,i2;int i4=0,i,i3;unsigned long length=10000;work=(char*)malloc(length*sizeof(char));fgets(work,length,codef);work2=(char*)malloc(length*sizeof(char));i3=2*n-1;for(i=0;*(work+i-1)!='\0';i++){i2=*(work+i);if(HT[i3].lchild==0){*(work2+i4)=*(z+i3-1);i4++;i3=2*n-1;i--;}else if(i2=='0') i3=HT[i3].lchild;else if(i2=='1') i3=HT[i3].rchild;}*(work2+i4)='\0';fputs(work2,txtfile);cout<<"…………译码完成…………"<<endl;cout<<"内容写入根目录下的文件text中"<<endl<<endl;free(work); //释放工作区free(work2); //释放工作区fclose(txtfile); //关闭文件txtfclose(codef); //关闭文件codef.txt}//-----------------------打印编码的函数----------------------void Code_printing(){cout<<"下面打印根目录下文件CodePrin.txt中编码字符"<<endl;FILE * CodePrin,* codefile;if((CodePrin=fopen("CodePrin.txt","w"))==NULL){cout<<"不能打开文件"<<endl;return;}if((code("code","r"))==NULL){cout<<"不能打开文件"<<endl;return;}char *work3;work3=(char*)malloc(51*sizeof(char));if(fgets(work3,51,code){cout<<"不能读取文件"<<endl;}elsedo{fputs(work3,CodePrin);puts(work3);}while(strlen(work3)==50&&fgets(work3,51,code);free(work3);cout<<"打印结束"<<endl<<endl;fclose(CodePrin);fclose(codefile);}//------------------------打印赫夫曼树的函数-----------------------void coprint(HuffmanTree start,HuffmanTree HT) //start=ht+26这是一个递归算法{if(start!=HT){FILE * TreePrint;if((TreePrint=fopen("TreePrint.txt","a"))==NULL){cout<<"创建文件失败"<<endl;return;}numb++; //number=0 该变量为已被声明为全局变量coprint(HT+start->rchild,HT); //递归先序遍历cout<<setw(5*numb)<<start->weight<<endl;fprintf(TreePrint,"%d\n",start->weight);coprint(HT+start->lchild,HT);numb--;fclose(TreePrint);}}void Tree_printing(HuffmanTree HT,int w){HuffmanTree p;p=HT+w; //p=HT+26cout<<"下面打印赫夫曼树"<<endl;coprint(p,HT); //p=HT+26cout<<"打印工作结束"<<endl;}//----------------------------------主函数-------------------------------------void main(){cout<<endl;cout<<" 此程序经晓光修改 "<<endl;cout<<" 实现赫夫曼编码解码功能 "<<endl;char choice;while(choice!='q'){ cout<<"\n******************************"<<endl;cout<<" 赫夫曼编码解码 "<<endl;cout<<"****************************** "<<endl;cout<<"(i)初始化赫夫曼表 "<<endl;cout<<"(w)输入待编码的字符 "<<endl;cout<<"(e)进行编码、译码、打印编码 "<<endl;cout<<"(t)打印赫夫曼树 "<<endl;cout<<"(q)离开 "<<endl;if(flag==0){cout<<"\n请先初始化赫夫曼链表,输入'i'"<<endl;cout<<"(程序将从根目录下的abc.txt文件中读出26个字母及其权值并对字母进行编码)"<<endl;}cin>>choice;switch(choice){case 'i':Initialization();//初始化赫夫曼表break;case 'w':InputCode(); //输入待编码的字符break;case 'e':Encoding();//进行编码Decoding();//进行译码Code_printing();//打印编码break;case 't':Tree_printing(HT,2*n-1);//打印26个字母权值形成的哈夫曼树break;case 'q': //退出程序break;default:cout<<"输入命令错误 "<<endl;。

相关主题