实验五.docx
- 文档编号:30760122
- 上传时间:2023-08-20
- 格式:DOCX
- 页数:38
- 大小:388.24KB
实验五.docx
《实验五.docx》由会员分享,可在线阅读,更多相关《实验五.docx(38页珍藏版)》请在冰豆网上搜索。
实验五
一、【实验构思(Conceive)】(10%)
(本部分应包括:
描述实验实现的基本思路,包括所用到的离散数学、工程数学、程序设计、算法等相关知识)
基本知识:
树是应用极为广泛的数据结构,树的基本理论和实验是本门课程的重点内容之一。
树结构的特点在于“非线性”。
本实验单元继续突出数据结构+操作构成抽象数据类型的程序设计观点,根据树结构非线性的特点,将操作进一步集中到树存储结构生成和遍历操作实现上,因为树的遍历操作是其他众多操作的基础。
树(tree)是包含n(n>0)个结点的有穷集合K,且在K中定义了一个关系N,N满足以下条件:
(1)有且仅有一个结点k0,他对于关系N来说没有前驱,称K0为树的根结点。
简称为根(root)。
(2)除K0外,k中的每个结点,对于关系N来说有且仅有一个前驱。
(3)K中各结点,对关系N来说可以有m个后继(m>=0)。
若n>1,除根结点之外的其余数据元素被分为m(m>0)个互不相交的结合T1,T2,……Tm,其中每一个集合Ti(1<=i<=m)本身也是一棵树。
树T1,T2,……Tm称作根结点的子树(subtree)。
赫夫曼树的概念
定义:
假设有n个权值{w1,w2,w3,……wn},试构造一棵具有n个叶子节点的二叉树,每个叶子结点带权为wi,则其中带权路径长度WPL最小的二叉树称作最优二叉树或赫夫曼树或哈夫曼树
设计思路及方案
本课题是用最优二叉树即哈夫曼树来实现哈夫曼编码译码器的功能。
假设每种字符在电文中出现的次数为Wi,编码长度为Li,电文中有n种字符,则电文编码总长度为(W1*L1)+(W2*L2)+…+(Wi*Li)。
若将此对应到二叉树上,Wi为叶结点,Li为根结点到叶结点的路径长度。
那么,(W1*L1)+(W2*L2)+…+(Wi*Li)恰好为二叉树上带权路径长度。
因此,设计电文总长最短的二进制前缀编码,就是以n种字符出现的频率作权,构造一棵哈夫曼树,此构造过程称为哈夫曼编码。
该系统将实现以下几大功能:
从硬盘读取字符串或直接输入,建立哈夫曼树,输出哈夫曼树的存储结构的初态和终态,输出各种字符出现的次数以及哈夫曼编码的译码,哈夫曼树的形态等。
二、【实验设计(Design)】(20%)
(本部分应包括:
抽象数据类型的功能规格说明、主程序模块、各子程序模块的伪码说明,主程序模块与各子程序模块间的调用关系)
抽象数据类型定义:
1、数据对象:
(1)用于存放哈夫曼编码的结构,包括字母,字母对应的哈夫曼编码,和哈夫曼编码的长度。
结构名为CodeNode
typedefstruct{
charch;
charbits[9];
intlen;
}CodeNode;
(2)用于存放赫夫曼树的结点,包括结点的权值,左孩子,右孩子,双亲的指针。
结构名为HTNode
typedefstruct{
intweight;
intlchild,rchild,parent;
}HTNode;
2、数据关系:
构造哈夫曼树
3、基本操作:
1、voidHuffmanEncoding(HuffmanTreeT,HuffmanCodeH)
操作结果:
初始化哈夫曼树,处理jsq(char*s,intcnt[],charstr[])函数,得到的数据,按照哈夫曼规则建立2叉树。
此函数块调用了Select()函数。
2、voidselect(HuffmanTreeT,intk,int&s1,int&s2)
操作结果:
在ht[1....k]中选择parent为0且权值最小的两个根结点的算法,其序号为s1和s2
主程序模块伪码说明:
主函数主要设计的是一个分支语句,让用户挑选所实现的功能。
分不同的情况,用户可以选择输入一段字符串,进行赫夫曼编码,或者选择从文件打开,对文件内容进行赫夫曼编码,或者选择直接退出。
、
主要调用函数:
num=jsq(string,cnt,str);//统计字符种类及各类字符出现的频率
DhuffmanTree(HT,cnt,str);
printf("HuffmanTree的初态:
\n");
print1(HT);//输出哈夫曼树的初态
ChuffmanTree(HT,HC,cnt,str);//建立哈夫曼树
HuffmanEncoding(HT,HC);//生成哈夫曼编码
printf("HuffmanTree的终态:
\n");
print2(HT);//输出哈夫曼树的终态
printf("\n哈夫曼树\n");//输出哈夫曼树
print(HT,2*num-1,0);
s=decode(HC);//读编码文件译码
printf("译码后的字符串:
\n");
printf("%s\n",s);//输出译码后的字符串
子程序模块的伪码说明:
Select函数:
选出HT树到a为止,权值最小且parent为0的2个节点,在ht[1....k]中选择parent为0且权值最小的两个根结点的算法,其序号为s1和s2。
Jsq函数:
统计字符串中各种字母的个数以及字符的种类。
建立一个有27个元素的数组,从第二个开始,进行循环,每遇到一个字母,对应的元素加1,最终,统计出每个字母的出现次数,并且,把生成的一个字母表和其对应的出现次数的统计表返回
ChuffmanTree:
构造哈夫曼树HT
先初始化HT,2*num-1是指哈夫曼树所有的结点数目,然后在ht[1....k]中选择parent为0且权值最小的两个根结点,其序号为s1和s2,i为双亲,用s1和s2生成一棵新的树,删除原有的两个节点,如此循环,直至生成整棵赫夫曼树。
这种结果生成之后,把每个节点对应的字母代入。
HuffmanEncoding:
根据哈夫曼树T求哈夫曼编码H,最后一位放上串结束符。
最终,对整棵树的所有节点都求出其赫夫曼编码。
Coding:
对str所代表的字符串进行编码并写入文件。
Outputcode:
显示出codefile.txt中的内容。
Decode:
读取codefile.txt的内容,并且将其翻译为字母,在屏幕上显示出字符串。
DhuffmanTree:
输入num个叶结点的权值。
各函数之间的调用关系
以打开文件进行赫夫曼编码为例:
首先,主程序调用fileopen,然后,主程序调用jsq函数对文本中的字母进行统计,然后,主程序调用DhuffmanTree函数给每个字母分配它对应的权值,然后调用coding函数,对str所代表的字符串进行编码,并写入codefile.txt文件,然后显示出赫夫曼树终态的情况,并把赫夫曼编码文件codefile.txt的内容显示出来。
然后主程序调用decode函数,对codefile.txt文件内容进行译码,显示出译码后的结果。
三、【实现描述(Implement)】(30%)
(本部分应包括:
抽象数据类型具体实现的函数原型说明、关键操作实现的伪码算法、函数设计、函数间的调用关系,关键的程序流程图等,给出关键算法的时间复杂度分析。
)
各函数的原型声明:
voidselect(HuffmanTreeT,intk,int&s1,int&s2)
intjsq(char*s,intcnt[],charstr[])
voidChuffmanTree(HuffmanTreeHT,HuffmanCodeHC,intcnt[],charstr[])
VoidHuffmanEncoding(HuffmanTreeT,HuffmanCodeH)
coding(HuffmanCodeHC,char*str)
har*decode(HuffmanCodeHC)
DhuffmanTree(HuffmanTreeHT,intcnt[],charstr[])
fileopen(charstring[])
关键操作实现的伪码算法:
1、建立HuffmanTree
(1)select
代码解释:
该函数为在ht[1....k]中选择parent为0且权值最小的两个根结点的算法,其序号为s1和s2。
(2)jsp
代码解释:
该函数用来统计字符串中各种字母的个数以及字符的种类。
当字符在A和Z之间时即被计数,并用str[j]保存字母到数组中,用cnt[j]统计每种字符个数。
j返回总共读取的字符数目。
用指针p指向,字符串,每次p加1,如果*p指向的位置是一个字母,则该字母对应的数组位置加1,经过循环,所有的字母出现次数都被统计出来。
再经过一个循环,把没有出现过的字母剔除掉,只在数组中保留出现过的字母。
(3)ChuffmanTree
代码解释:
下面函数用来构造哈夫曼树HT。
首先初始化哈夫曼树,然后输入前面统计的各结点的权值,用for循环来构造哈夫曼树。
2、生成Huffman编码并写入文件
(1)HuffmanEncoding
代码解释:
根据哈夫曼树T求哈夫曼编码H。
(2)coding
代码解释:
对str所代表的字符串进行编码并写入文件。
将翻译的二进制码写入文本文件。
3、电文译码
(1)decode
代码解释:
代码文件codefile.txt的译码,将翻译的二进制码译成原来的字符。
4、打印
(1)print1
代码解释:
打印哈夫曼树的初态
(2)print2
代码解释:
打印哈夫曼树的终态
(3)Print
代码解释:
打印哈夫曼树的形态
流程图:
①主函数流程图:
流程图注释:
该图主要是调用各个函数模块,首先选择是打开文件还是输入字符串,然后统计总的字符数以及出现的各个字符和频率。
然后才开始建立哈夫曼树,接着在哈夫曼树的基础上对其进行编码,编码之后才是译码。
最后输出结束。
输入字符串
②构造哈夫曼树:
流程图注释:
该图是表示构造哈夫曼树的过程。
首先输入num个叶结点的权值,当i=num是循环结束。
然后进行哈夫曼树的构建,当i=2*num-1是循环结束。
最后输出所得到的字符统计情况。
③哈夫曼编码:
流程图解释:
该流程图表四哈夫曼编码情况。
首先初始化,Cd[--start]=0,start=num。
然后进行
编码,使用了一个三目运算符。
cd[--start]=(T[p].lchild==c)?
'0':
'1',即当cd[--start]=T[p].lchild==c时,cd[--start]=0;当cd[--start]=T[p].lchild!
==c时,cd[--start]=1。
这个编码循环一直到i=num时结束。
时间复杂度:
voidselect(HuffmanTreeT,intk,int&s1,int&s2)时间复杂度n
intjsq(char*s,intcnt[],charstr[])n
voidChuffmanTree(HuffmanTreeHT,HuffmanCodeHC,intcnt[],charstr[])n
VoidHuffmanEncoding(HuffmanTreeT,HuffmanCodeH)n^2
coding(HuffmanCodeHC,char*str)n^3
har*decode(HuffmanCodeHC)n^3
DhuffmanTree(HuffmanTreeHT,intcnt[],charstr[])n
fileopen(charstring[])n
四、【测试结果(Testing)】(10%)
(本部分应包括:
对实验的测试结果,应具体列出每次测试所输入的数据以及输出的数据,并对测试结果进行分析总结)
未加界面前:
1、进入界面
选择要进行的操作
2、选择第一项功能时:
(1)输入要编码的字符
(2)输出哈夫曼树的初态
(3)输出统计字符信息
(4)输出哈夫曼树终态
(5)输出编码后的语句
(6)输出译码后的语句
3、选择第二项操作:
将打开的文件编码译码
测试字符串的内容为:
THISPROGRAMISMYFAVORITE
加图形化界面后
1、登录界面
列出文件菜单,用户可以自主选择
2、选择初始化功能
3、选择文件打开功能
4、功能概述
(1)打开菜单
(2)保存菜单
(3)提示菜单
(4)关于菜单
四、【实验总结】(10%)
(本部分应包括:
自己在实验中完成的任务,注意组内的任意一位同学都必须独立完成至少一项接口的实现;对所完成实验的经验总结、心得)
这次实验主要是对树有关知识的应用和实践,尤其是赫夫曼树。
通过这次实验,使我们对树的概念有了更深刻的了解,认识到树是应用极为广泛的数据结构,其应用领域主要有:
表示源程序的语法机构,实现数据在存储介质比如硬盘上的存取,进行赫夫曼编码,将所需传送的文字转换成由二进制的字符组成的字符串,实现快速远距离通信等。
在这次试验中我们实现了:
建立二叉树,并实现二叉树的遍历。
在此基础上,实现树与二叉树的相互转换。
实现一个简单的哈夫曼编码/译码器,能根据一段电文设计赫夫曼编码,并能用该编码对一段给定的电文进行译码。
李文婷:
这次试验中,使我学到了利用树这种结构实现赫夫曼编码,学到了建立二叉树,并实现二叉树的遍历,实现树与二叉树的相互转换,等树的基本操作。
在实验中,我遇到了一些问题,比如由于对文件的读取函数有些生疏,不能顺利的读取出文件中的内容,对空格的统计问题等。
通过翻阅以前的教科书,我重新掌握了文件读取函数的使用方法,如fopen,fgets,fgetc等。
最终,解决了这些问题,完成了本次试验。
再实现图形界面的过程中,我又重新学习了API的使用方法,对图形化界面的制作更加熟练,对消息机制等windows编程概念有了更深的理解。
李通:
在实验中,我对树的概念有了深刻的了解,学到了建立二叉树,并实现二叉树的遍历,实现树与二叉树的相互转换的方法,能够实现对树的各种操作。
遇到的问题有,对文件的读写无法实现,由于变量太多,总是忘记一些变量的类型和它的作用。
通过翻阅旧的教材,我最终实现了文件的读写。
通过在编译器中使用“跳至声明、定义,引用”,我基本可以在忘记变量含义的时候重新回忆起来,这个过程中,也是我对编译器的使用更加熟练。
在调试的过程,由于有一些特殊需要,比如,在循环中执行一定次数之后停止,我只能在网上搜索调试的高级用法,最终,可以对调试过程使用的得心应手。
姚林:
在这次实验中,我对参数的调用也进行了很多种尝试,已经能够相对准确的选择合适的参数形式来实现函数之间的数据传输交互了。
体会到深刻理解数据结构的重要性,只有真正理解这样定义数据类型的好处,才能用好这样一种数据结构。
了解典型数据结构的性质是非常有用的,它往往是编写程序的关键。
通过这次试验,我们已经基本掌握了树结构的运用,为以后的编程打下了基础,而且,也是我们对制作图形化界面更有信心。
五、【项目运作描述(Operate)】(10%)
(本部分应包括:
项目的成本效益分析,应用效果等的分析。
)
项目成本效益分析:
本实验无额外成本,主要在于时间安排,基本集中在课外尤其是周末时间。
各员工(同学)都尝到了熬夜痛苦的滋味,深知预先做好项目管理的重要性,牢记一定要严格控制工期和合理安排进度。
应用效果分析:
程序做出来之后,我们把它交给身边的一些同学使用,以收集同学们的使用体验,进一步修改程序,使程序更加符合用户的需求,提高用户的用户的满意度和使用的舒适感。
同学们的感受:
能满足他们基本编码译码的需求,界面美观大方。
改进意见:
程序界面可以打印出更多的东西,比如哈夫曼树的形态等。
调查结果将后来附上。
六、【代码】(10%)
(本部分应包括:
完整的代码及充分的注释。
注意纸质的实验报告无需包括此部分。
格式统一为,字体:
Georgia,行距:
固定行距12,字号:
小五)
#include
#include
#include
usingnamespacestd;
typedefstruct{
charword;
unsignedintweight;
unsignedintparent,lchild,rchild;
}HTNode,*HuffmanTree;
typedefchar**HuffmanCode;
classHuffmansystem{
public:
Huffmansystem();
voidInitial();//哈夫曼树的初始化
voidEncoding();//报文的编码函数
voidDecoding();//报文的译码函数
voidSelect(HuffmanTree,int,int&,int&);//从哈夫曼树中选两点权值最小的节点
voidstrcpy(char*,char*);//
intcompare(char*,char*);//比较两个数组中所存的字符串是否相等
voidprint();//打印编码后的文件
voidTreeprint();
private:
HuffmanTreeHT;//此结构存储哈夫曼树
HuffmanCodeHC;//此结构存储哈夫曼编码每个字符的
char*cd;//辅助变量用来给HC[i]中每个编码赋值
intn;//字符集的大小
intm;//哈夫曼树的节点数
strings;//辅助变量,用来存储当前内存中的哈夫曼树的名称
inttag;//标志是否进行过初始化操作
};
Huffmansystem:
:
Huffmansystem(){
cout<<"菜单项"< cout<<"初始化I编码E译码D印代码文件P印哈夫曼树T退出Q"< chara; cin>>a; while(a){ if(a=='Q'){ if(tag==1){ deletecd; deleteHC; } break; } switch(a){ case'I': Initial();break; case'E': Encoding();break; case'D': Decoding();break; case'P': print();break; case'T': Treeprint();break; default: cout<<"您的输入有误"; } cout<<"请输入所需的操作"< cout<<"初始化I编码E译码D印代码文件P印哈夫曼树T退出Q"< cin>>a; } }//编码译码的接口操作 voidHuffmansystem: : Initial(){ if(tag==1){ cout<<"请输入您所要读取的哈夫曼树的文件名"< intN=0,temp; chartemp1,tt,T;//tt内装有回车字符 charfilename[15]; cin>>filename; cin.get(tt); ifstreamfin; fin.open(filename); while(! fin){ cout<<"没打开"< fin.close(); fin.clear(); cout<<"请重新输入文件名"< cin>>filename; fin.open(filename); } s=filename; while(fin.get(temp1)){ N++; if(temp1==tt){ fin.get(T); } for(inti=1;i<=4;i++){ fin>>temp; N++; } } fin.close(); fin.clear(); n=((N/5)+1)/2; m=2*n-1; HT=newHTNode[m+1]; ifstreamfin1; fin1.open(filename); if(! fin1){ cout<<"运行错误"< exit(0); } chartemp2; for(inti=1;i<=m;i++){ fin1.get(HT[i].word); if(HT[i].word==tt){ } fin1>>HT[i].weight; fin1>>HT[i].parent; fin1>>HT[i].lchild; fin1>>HT[i].rchild; if(i! =m){ fin1.get(temp2); } } fin1.close(); tag=2; }//从文件中读入哈夫曼树 else{ cout<<"当前内存没有哈夫曼树,请自行读入"< cout<<"输入字符集的大小"; cin>>n; if(n<=1){ cout<<"你的字符集大小不合法"< cout<<"请重新输入"< } m=2*n-1; HT=newHTNode[m+1]; chara,temp; intw; cin.get(temp); for(inti=1;i<=n;i++){ cout<<"输入第"< cin.get(a); cin>>w; cin.get(temp); HT[i].word=a; HT[i].weight=w; HT[i].parent=HT[i].lchild=HT[i].rchild=0; } for(inti=n+1;i<=m;i++){ HT[i].word='O'; HT[i].weight=0; HT[i].parent=HT[i].lchild=HT[i].rchild=0; } for(inti=n+1;i<=m;i++){ ints1,s2; Select(HT,i-1,s1,s2); HT[s1].parent=i; HT[s2].parent=i; if(s1>s2){ inttemp; temp=s1; s1=s2; s2=temp; } HT[i].lchild=s1; HT[i].rchild=s2; HT[i].weight=HT[s1].weight+HT[
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 实验
![提示](https://static.bdocx.com/images/bang_tan.gif)