数据结构课程设计实验报告哈夫曼树的应用.docx
《数据结构课程设计实验报告哈夫曼树的应用.docx》由会员分享,可在线阅读,更多相关《数据结构课程设计实验报告哈夫曼树的应用.docx(24页珍藏版)》请在冰豆网上搜索。
数据结构课程设计实验报告哈夫曼树的应用
计算机学院信管专业
数据结构课程设计
题目:
哈夫曼树的应用
班级:
姓名:
学号:
同组人姓名:
起迄日期:
课程设计地点:
指导教师:
评阅意见:
成绩评定:
评阅人:
日期:
完成日期:
2012年12月
一、需求分析…………………………………………3
二、概要设计…………………………………………4
三、详细设计…………………………………………6
四、调试分析和测试结果……………………………7
五、心得体会和总结………………………………10
六、参考文献………………………………………10
七、附录……………………………………………11
一、需求分析
(一)实验要求
要求用到数据结构课上学到的线性表的知识,所以就要充分而清晰的理解关于线性表的知识。
要求实现的基本功能很简单,只有删除和插入,增加功能也不过是加上修改。
这些在数据结构课上已经讲过,只要能够理解关于线性表的几个相关的基本算法就可以了。
问题是将输入的信息保存入文件和从文件输出。
这里基本是自学的内容,而且要考虑到是否要自行选择保存的磁盘。
综上,做这个课题,要具备的知识就是线性表的基本算法,文件的保存和读取算法,必要的C或者C++知识(本次我将使用C++实现),以及丰富的程序调适经验。
(二)实验任务
一个完整的系统应具有以下功能:
功能1.从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树并将它存于文件hfmTree中.将已在内存中的哈夫曼树以直观的方式(比如树)显示在终端上;
功能2.利用已经建好的哈夫曼树(如不在内存,则从文件htmTree中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中,并输出结果,将文件CodeFile以紧凑格式先是在终端上,每行50个代码。
同时将此字符形式的编码文件写入文件CodePrint中。
功能3.利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件TextFile中,并输出结果。
(三)实验步骤
分步实施:
1)初步完成总体设计,搭好框架,确定人机对话的界面,确定函数个数;
2)完成最低要求:
完成功能1;
3)进一步要求:
完成功能2和3。
有兴趣的同学可以自己扩充系统功能。
要求:
1)界面友好,函数功能要划分好
2)总体设计应画一流程图
3)程序要加必要的注释
4)要提供程序测试方案
5)程序一定要经得起测试,宁可功能少一些,也要能运行起来,不能运行的程序是没有价值的。
二、概要设计
(一)设计思想
哈夫曼树用邻接矩阵作为存储结构,借助静态链表来实现遍历。
(二)函数间的关系如图所示:
(三)数据结构与算法设计
哈夫曼编\译码器的主要功能是先建立哈夫曼树,然后利用建好的哈夫曼树生成哈夫曼编码后进行译码。
在数据通信中,经常需要将传送的文字转换成由二进制字符0、1组成的二进制串,称之为编码。
构造一棵哈夫曼树,规定哈夫曼树中的左分之代表0,右分支代表1,则从根节点到每个叶子节点所经过的路径分支组成的0和1的序列便为该节点对应字符的编码,称之为哈夫曼编码。
最简单的二进制编码方式是等长编码。
若采用不等长编码,让出现频率高的字符具有较短的编码,让出现频率低的字符具有较长的编码,这样可能缩短传送电文的总长度。
哈夫曼树课用于构造使电文的编码总长最短的编码方案。
其主要流程图如下图所示。
哈夫曼树编\译码器流程图
三、详细设计
功能函数模块划分
voidmain()
voidprinthead()
voidprintree(HuffmanTreeHT,intw)//打印赫夫曼树
voidcoprint(HuffmanTreestart,HuffmanTreeHT)//打印代码文件
voidprintcode()//打印代码
voiddecode()//完成译码功能
voidencode()//完成编码功能
voidinputcode()
voidinit()
voidHuffmanCoding(HuffmanTree&HT,HuffmanCode&HC,int*w,intn)
voidselect(HuffmanTreet,inti,int&s1,int&s2)
intmin(HuffmanTreet,inti)//找两个最小的权值
(1)哈夫曼编码:
首先定义函数,找出全部权值中最小的两个,然后定义一个变量,使他始终成为最小的那个。
再将两个函数最为叶子结点,并得到一个父亲节点,此父亲节点的权值为其叶子节点的权值之和。
并将此父亲节点的权值与其余权值进行比较,重新选出两个最小的权值,再进行上述步骤,直到所有权值形成了一颗二叉树,而此二叉树就是我们所说的最优二叉树,即哈夫曼树。
以上为哈夫曼树的建立过程,下面为哈夫曼编码的过程,从叶子节点出发,若此叶子节点为其父亲节点的左孩子,则将其编码为0,若为右孩子,则将其编码为1,然后为其父亲节点编码,若为祖先的左孩子,则变为0,为右孩子则为1,依次向上一层进行遍历,直到遍历到根节点,停止编码。
(2)译码:
对于已经建好的哈夫曼树,要对其进行译码,首先从根出发如果编码为0,则往左孩子遍历,如果编码为1,则往右孩子遍历,直到遍历到叶子节点,便求得该子串相应的字符。
(3)初始化哈夫曼链表:
首先输入结点个数,再将字符及权值输入,调用编码函数,得到每个字符编码并将其输出。
最后将哈夫曼编码写入文件。
(4)完成编码功能:
打开目录下文件tobetran.txt,读取里面的字符,对其进行编码后,将编码写入目录下的codefile.txt中。
(5)完成译码功能:
打开根目录下codefile.txt文件,读取里面的编码,对其中的编码进行译码,并将得到的内容写入根目录下的文件txtfile.txt中。
(6)打印编码
(7)打印哈夫曼树
四、调试分析和测试结果
(一)初始化哈夫曼链表
(二)编码字符
(三)编码
(四)译码
(五)打印编码
(六)打印哈夫曼函数
五、心得体会与总结
对于本次课程设计,主要是需要掌握哈夫曼树建立、哈夫曼编码以及哈夫曼译码的算法。
并能将其熟练应用于编译码器的完成。
经过这次的课程设计,使我们更加了解了数据结构,也更深入地了解了哈夫曼编码与译码算法,课程设计的题目比我们平常的实验内容要难,完成它不仅需要有厚实的语言基础,而且还要熟练掌握哈夫曼编码与译码的算法,另外对于文件的基本操作也需要熟悉。
通过数据结构课程设计,我的C++语言水平有了比较大的提高其中。
C++语言关于类的操作理解的比以前深刻不少。
另外是数据结构方面的提高对哈夫曼树的构造及哈夫曼码方面也有不少的提高。
在项目中也出现了很多的问题,最大的问题就是对程序设计框架结构的不了解,在实现代码与功能的连接时经常会出现各种不同的错误,在实现一些功能时系统常常会报错。
许多错误不知从哪修改以致拖了整个设计的后腿。
课程设计中,既回顾了很多以前的东西,也发现了很多的问题以前都没遇见过的,收获很大。
通过本次数据结构的课程设计,我学习了很多在上课没懂的知识,并对求哈夫曼树及哈夫曼编码/译码的算法有了更加深刻的了解,更巩固了课堂中学习有关于哈夫曼编码的知识。
此次哈夫曼树的应用系统的设计让自己对数据结构的了解更深入。
六、参考文献
《C++面向对象程序设计教程》(第三版)陈维兴林小茶编著清华大学出版社
《数据结构》(C语言版)严蔚民吴伟民编著清华大学出版社
六、附录
源程序:
#include
#include
#include
#include
#include
constintUINT_MAX=1000;
typedefstruct//哈夫曼树的存储表示
{
intweight;//权值
intparent,lchild,rchild;//父节点,左孩子结点,右孩子结点
}HTNode,*HuffmanTree;//动态分配数组存储哈夫曼树
typedefchar**HuffmanCode;//动态分配数组存储哈夫曼编码表
//-----------全局变量-----------------------
HuffmanTreeHT;//代表哈夫曼树
HuffmanCodeHC;//代表哈夫曼编码
int*w,i,j,n;
char*z;
intflag=0;
intnumb=0;
//-----------------求哈夫曼编码-----------------------
voidline()//画分割线的函数
{
cout<<"\n--------------------------------------------------\n";
}
intmin(HuffmanTreet,inti)//找两个最小的权值
{
intj,flag;
intk=UINT_MAX;//取k为不小于可能的值
for(j=1;j<=i;j++)
if(t[j].weightk=t[j].weight,flag=j;
t[flag].parent=1;
returnflag;//返回标识符
}
//--------------------使s1成为最小权值----------------------
voidselect(HuffmanTreet,inti,int&s1,int&s2)
{
intj;
s1=min(t,i);
s2=min(t,i);
if(s1>s2)//s1为最小的两个值中序号较小的那个
{
j=s1;
s1=s2;
s2=j;
}
}
voidHuffmanCoding(HuffmanTree&HT,HuffmanCode&HC,int*w,intn)
{
intm,i,s1,s2,start;
intc,f;
HuffmanTreep;
char*cd;
if(n<=1)
return;
m=2*n-1;//申请2n-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)//建哈夫曼树
{
select(HT,i-1,s1,s2);//调用建子树的函数
HT[s1].parent=HT[s2].parent=i;//i是s1和s2的父节点
HT[i].lchild=s1;
HT[i].rchild=s2;//s1和s2是i的儿子节点
HT[i].weight=HT[s1].weight+HT[s2].weight;//i的权值为s1和s2的和
}
HC=(HuffmanCode)malloc((n+1)*sizeof(char*));//分配n个字符编码的头指针向量
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';
else
cd[--start]='1';
HC[i]=(char*)malloc((n-start)*sizeof(char));//为第i个字符编码分配空间
strcpy(HC[i],&cd[start]);//从cd复制编码(串)到HC
}
free(cd);//释放工作空间
}
//--------------初始化哈夫曼链表---------------------------------
voidinit()
{
flag=1;
intnum;
intnum2;
cout<<"下面初始化哈夫曼链表"<";
cin>>num;//输入结点个数
n=num;
w=(int*)malloc(n*sizeof(int));//权值
z=(char*)malloc(n*sizeof(char));//字符
cout<<"\n请依次输入"<必须以回车结束:
"<chartemp[2];
for(i=0;i{
cout<<"第"<
"<gets(temp);
*(z+i)=*temp;
}
line();
for(i=0;i<=n-1;i++)//输出字符
{
cout<}
line();
cout<<"\n请依次输入"<必须以回车结束):
"<for(i=0;i<=n-1;i++)//输入权值
{
cout<";
cin>>num2;
*(w+i)=num2;
}
HuffmanCoding(HT,HC,w,n);//调用哈夫曼编码
//------------------------打印编码----------------------
cout<<"字符对应的编码为:
"<for(i=1;i<=n;i++)//输出所有编码
{
puts(HC[i]);
}
//--------------------------将哈夫曼编码写入文件---------
cout<<"下面将赫夫曼编码写入文件"<FILE*htmTree;
charr[]={'','\0'};
if((htmTree=fopen("htmTree.txt","w"))==NULL)
{
cout<<"文件打开失败"<return;
}
fputs(z,htmTree);
for(i=0;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中"<}//init
//---------------------获取字符并写入文件---------------------------------
voidinputcode()
{
FILE*virttran,*tobetran;
charstr[100];
if((tobetran=fopen("tobetran.txt","w"))==NULL)
{
cout<<"不能打开文件"<return;
}
cout<<"请输入你想要编码的字符"<gets(str);
fputs(str,tobetran);
cout<<"获取字符成功"<fclose(tobetran);
}
//------------------------------------------------------
voidencode()//完成编码功能
{
cout<<"下面对目录下文件tobetran.txt中的字符进行编码"<FILE*tobetran,*codefile;
if((tobetran=fopen("tobetran.txt","rb"))==NULL)
{
cout<<"不能打开文件"<}
if((codefile=fopen("codefile.txt","wb"))==NULL)
{
cout<<"不能打开文件"<}
char*tran;
i=99;
tran=(char*)malloc(100*sizeof(char));//为tran分配100个字节
while(i==99)
{
if(fgets(tran,100,tobetran)==NULL)
{
cout<<"不能打开文件"<break;
}
for(i=0;*(tran+i)!
='\0';i++)
{
for(j=0;j<=n;j++)
{
if(*(z+j-1)==*(tran+i))
{
fputs(HC[j],codefile);
puts(HC[j]);
if(j>n)
{
cout<<"字符错误,无法编码!
"<break;
}
}
}
}
}
cout<<"编码工作完成"<fclose(tobetran);
fclose(codefile);
free(tran);
}
//--------------------------------------------------
voiddecode()//完成译码功能
{
cout<<"下面对根目录下文件codefile.txt中的字符进行译码"<FILE*codef,*txtfile;
if((txtfile=fopen("Textfile.txt","w"))==NULL)
{
cout<<"不能打开文件"<}
if((codef=fopen("codefile.txt","r"))==NULL)
{
cout<<"不能打开文件"<}
char*tbdc,*outext,i2;
intio=0,i,m;
unsignedlonglength=10000;
tbdc=(char*)malloc(length*sizeof(char));//分配空间
fgets(tbdc,length,codef);
outext=(char*)malloc(length*sizeof(char));//分配空间
m=2*n-1;
for(i=0;*(tbdc+i)!
='\0';i++)//进入循环
{
i2=*(tbdc+i);
if(HT[m].lchild==0)
{
*(outext+io)=*(z+m-1);
io++;
m=2*n-1;
i--;
}
elseif(i2=='0')m=HT[m].lchild;
elseif(i2=='1')m=HT[m].rchild;
}
*(outext+io)='\0';
fputs(outext,txtfile);
cout<<"译码完成"<free(tbdc);
free(outext);
fclose(txtfile);
fclose(codef);
}
//---------------------------------------------
voidprintcode()//打印代码
{
cout<<"下面打印根目录下文件CodePrin.txt中编码字符"<FILE*CodePrin,*codefile;
if((CodePrin=fopen("CodePrin.txt","w"))==NULL)
{
cout<<"不能打开文件"<return;
}
if((codefile=fopen("codefile.txt","r"))==NULL)
{
cout<<"不能打开文件"<return;
}
char*work3;
work3=(char*)malloc(51*sizeof(char));
do
{
if(fgets(work3,51,codefile)==NULL)
{
cout<<"不能读取文件"<break;
}
fputs(work3,CodePrin);
puts(work3);
}while(strlen(work3)==50);
free(work3);
cout<<"打印工作结束"<fclose(CodePrin);
fclose(codefile);
}
voidcoprint(HuffmanTreestart,HuffmanTreeHT)//打印代码文件
{chart='';
if(start!
=HT)
{
FILE*TreePrint;
if((TreePrint=fopen("TreePrint.txt","a"))==NULL)
{
cout<<"创建文件失败"<return;
}
numb++;//该变量为已被声明为全局变量
coprint(HT+start->rchild,HT);
if(start->lchild!
=NULL&&start->rchild!
=NULL)t='<';
cout<weight<fprintf(TreePrint,"%d\n",start->weight