哈夫曼树课程设计报告材料.docx
《哈夫曼树课程设计报告材料.docx》由会员分享,可在线阅读,更多相关《哈夫曼树课程设计报告材料.docx(17页珍藏版)》请在冰豆网上搜索。
![哈夫曼树课程设计报告材料.docx](https://file1.bdocx.com/fileroot1/2023-1/8/56d38ce7-4ece-478b-a573-72249848f151/56d38ce7-4ece-478b-a573-72249848f1511.gif)
哈夫曼树课程设计报告材料
课程设计
题目:
哈夫曼编码器
院系:
专业班级:
学号:
学生姓名:
指导教师:
2014年1月2日
课程设计需求分析报告
一、分析问题和确定解决方案
1.分析问题
利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。
但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。
对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统,为这样的信息收发站写一个哈夫曼的编/译码系统。
2.确定解决方案
设计建立带权的哈夫曼树,确定哈夫曼树的类与成员函数,以及各函数之间的调用关系,采用动态数组的存储结构存储所需要的数据,通过不同的函数来实现编码,译码以及打印二进制编码、哈夫曼树,把不同的数据存入不同的txt文件中,通过主函数调用来实现功能检测。
3.输入的形式和输入值的范围
手动或者从文本中读入数据的形式初始化哈夫曼树,从键盘中或者文件中读入数据,以字母A-Z代表结点,以自然数代表权值,字符串提示使用者所要执行的操作。
4.输出的形式
在显示器界面上或者以文本的形式来实现程序调试的输出。
5.程序所能达到的功能
(1)初始化。
手动输入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件WritehfmTree中,输出哈夫曼树及各字符对应的编码存于WritehfmCode;从文本中读入字符,建立哈夫曼树存于ReadhfmTree,输出哈夫曼树及各字符对应的编码存于ReadhfmCode.
(2)编码。
手动输入一串大写英文字符,该字符存于WriteToBeTron中,对字符进行编码并将它存于WriteCodeFile中;从文件中读取字符编码并存于ReadCodeFile中。
(3)印代码文件。
将文件ReadCodeFile以紧凑格式显示在终端上,每行50个代码。
同时将此字符形式的代码码写入文件CodePrint中。
(4)印哈夫曼树。
将初始化的哈夫曼树以直观的方式显示在终端上,同时将此字符形式的哈夫曼树写入文件TreePrint中。
各个功能数据输出存储位置(如表1所示)
表1:
各个功能数据输出存储位置表
功能
字母
二进制码
初始化
WritehfmTree(手动)
WritehfmCode(手动)
ReadhfmTree(文本读入)
ReadhfmCode(文本读入)
hfmTree(默认文本读入)
hfmCode(默认文本读入)
编码
WriteToBeTron(手动)
WriteCodeFile(手动)
ReadCodeFile(文本读入)
印编码代码
CodePrint
印哈夫曼树
TreePrint
6.测试数据
(1)正确的输入:
1>输入主菜单项中的英文字母I(i),E(e),D(d),P(p),Q(q)
输出结果:
进入所选的功能界面
2>输入子菜单项中的数字1,2,3,(4)
输出结果:
执行所选的功能
(2)含有错误的输入:
1>输入除了主菜单项中的英文字母I(i),E(e),D(d),P(p),Q(q)
输出结果:
<您的输入有误,请重新输入:
>
2>输入除了子菜单项中的数字1,2,3,(4)
输出结果:
<您的输入有误,请重新输入:
>
7.程序说明
(1)程序中数据类型的定义:
用到三组结构体,分别是哈夫曼树的动态数组存储结构*HuffmanTree,哈夫曼编码表的存储结构HuffmanCode,字符结点的动态数组存储结构wElem以及哈夫曼树类定义classHuffman。
(2)主程序的流程图:
用户从主菜单中选择所要进行的操作,如果输入选项错误则提示重新输入选项,否则进入选中的操作项(如图1所示)。
图1:
主程序流程图
(3)各程序模块之间的层次(调用)关系:
主函数main()调用初始化,编码,译码,打印二进制编码,打印哈夫曼树这五个子函数;进入初始化功能后调用手动输入,文本读入,默认文本这三个函数;进入编码功能后调用手动编码,文本读入编码这两个函数;进入译码功能后调用手动译码,文本读入译码这两个函数(如图2所示)。
图2:
:
各程序模块之间的层次(调用)关系
(4)默认的哈夫曼树:
空格以及字母A—Z频度分别为186,64,13,22,32,103,21,15,47,57,1,5,32,20,57,63,15,1,48,51,80,23,8,18,1,16,1建立一棵默认的哈夫曼树(如图3所示)。
图3:
默认的哈夫曼树
二、详细设计
1、哈夫曼树存储及类的定义:
#include
#include
#include
#include
#include
typedefstruct//哈夫曼树的存储结构
{
intweight;//权值
charHTch;//字符
intparent,lchild,rchild;//双亲,左孩子,右孩子
}HTNode,*HuffmanTree;//动态数组存储哈夫曼树
typedefstruct//哈夫曼编码表的存储结构
{
charch;//字符
char*hufCh;//二进制码
}HuffmanCode;//动态数组存储哈夫曼编码表
typedefstruct//字符结点
{
charch;//字符
intwt;//字符权值
}wElem;//动态分配数组存储读入字符与权值
classHuffman
{
public:
Huffman(){};//构造函数
~Huffman(){};//析构函数
voidInitialization(HuffmanTree&HT,HuffmanCode*HC,int&n);//初始化,手动
voidInitialization(HuffmanTree&HT,HuffmanCode*HC,int&n,intv);
//初始化,标准文件
voidInitialization(HuffmanTree&HT,HuffmanCode*HC,char*InitFile,int&n);
//初始化,统计
voidEnCoding(HuffmanCode*HC,inthufnum);//手动编码
voidEnCoding(HuffmanCode*HC,char*EnCodeFile);//文件读入编码
voidPrint(char*);//打印二进制编码
voidTreeprinting(HTNodeT,HuffmanTreeHT,intn);//打印哈夫曼树
};
2、哈夫曼树的基本操作:
//手动输入字符与权值并初始化
voidHuffman:
:
Initialization(HuffmanTree&HT,HuffmanCode*HC,int&n)//哈夫曼树对象,编码对象,字符数
//从文件读入标准哈夫曼树并初始化
voidHuffman:
:
Initialization(HuffmanTree&HT,HuffmanCode*HC,int&n,intv)//哈夫曼树对象,编码对象,字符数,区分功能参数
//从文件中统计字符与权值,构造哈弗曼树
voidHuffman:
:
Initialization(HuffmanTree&HT,HuffmanCode*HC,char*InitFile,int&n)//哈夫曼树对象,编码对象,文件名,字符数
//编码函数,对用户输入的文件的正文进行编码,然后将结果存入文件WriteCodeFile.txt中
voidHuffman:
:
EnCoding(HuffmanCode*HC,inthufnum)//编码数组对象,字符数
//编码函数,从文件读取
voidHuffman:
:
EnCoding(HuffmanCode*HC,char*EnCodeFile)//编码数组对象,文件名
//译码函数,对文件CodeFile中的代码进行译码,结果存入文件ReadTextFile.txt中
voidHuffman:
:
DeCoding(HuffmanTreeHT,HuffmanCode*HC,char*DeCodeFile,intn)//哈夫曼树对象,编码对象,文件名,字符数
//译码函数,手动输入
voidHuffman:
:
DeCoding(HuffmanTreeHT,HuffmanCode*HC,intn)//哈夫曼树对象,编码对象,字符数
//打印函数,将文件CodePrint.txt中的内容以每行个代码显示在屏幕上
voidHuffman:
:
Print(char*cfileName)//文件名
//打印哈夫曼树
voidcoprint(HuffmanTreestart,HuffmanTreeHT)//哈夫曼树对象,哈夫曼树对象
其中部分操作的伪代码如下:
(1)从文件读入标准哈夫曼树并初始化
voidHuffman:
:
Initialization(HuffmanTree&HT,HuffmanCode*HC,int&n,intv)//哈夫曼树对象,编码对象,字符数,区分功能参数
{定义一个动态数组存放空格和26个英文字母,把字符串"ABCDEFGHIJKLMNOPQRSTUVWXYZ"读入文件"CharFile.txt"
while(charRead.get(inbuf))
{
w[j].ch=inbuf;
w[j].wt=cw[j];
j++;
}
//w存放n字符及其权值(从0号单元开始),构造哈夫曼树HT,并求出n个字符的哈夫曼编码HC.
inti,m,ww=0;//n:
字符数m:
树结点数
ints1,s2;
HuffmanTreep;//定义指针变量p
if(n<=1)return;//小于2个字符,结束
m=2*n-1;//n个叶子,2*n-1个结点
HT=newHTNode[(m+1)*sizeof(HTNode)];
HT[0].parent=-1;HT[0].lchild=-1;HT[0].rchild=-1;HT[0].weight=0;
for(p=HT+1,i=1;i<=n;i++,p++,ww++)//初始化n个叶子结点(即n个字符)
{
p->weight=w[ww].wt;
p->HTch=w[ww].ch;
p->parent=p->lchild=p->rchild=0;
}//跳出循环时i=n+1;
for(;i<=m;i++,++p)//初始化叶子结点之外的其它所有结点
{
p->weight=0;
p->HTch='#';
p->parent=p->lchild=p->rchild=0;
}
for(i=n+1;i<=m;i++)//建立哈夫曼树
{
Select(HT,i-1,s1,s2);//在HT数组的至i-1个结点中选择parent为且权值weight最小的两个结点,其序号分别为S1和S2;
HT[s1].parent=i;
HT[s2].parent=i;
HT[i].lchild=s1;
HT[i].rchild=s2;
HT[i].weight=HT[s1].weight+HT[s2].weight;
}
char*cd=newchar[n];//分配编码的存储空间
cd[n-1]='\0';//编码结束符
intc,f,start;
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].ch=w[i-1].ch;//复制字符
HC[i].hufCh=newchar[(n-start)*sizeof(char)];//为第i个字符编码分配空间
strcpy(HC[i].hufCh,&cd[start]);//从cd复制编码(串)到HC
}
//向屏幕输出哈夫曼编码,并把编码保存在文件hfmCode.txt中;
}
(2)编码函数,从文件读取
voidHuffman:
:
EnCoding(HuffmanCode*HC,char*EnCodeFile)//编码数组对象,文件名
{对文件进行编码,并将编码存于文件ReadCodeFile.txt中
while(ufileRead.get(charInbuf))
{
for(intk=1;k<=27;k++)
{
if(charInbuf==HC[k].ch)
{
codeWrite<}
}
}
}
3、主函数:
#include"Huffman.cpp"
//主函数
voidmain()
{intcurrent_n=27;//全局变量,字符数
charc;//功能选择
inthufnum=27;//默认字符数
HuffmanTreeHT;//哈夫曼树对象
HuffmanCode*HC=newHuffmanCode[(hufnum+1)*sizeof(HuffmanCode)];//分配n个
//字符编码的头指针向while
(1)
{//主菜单
cout<<"请按顺序选择要实现的功能:
";
intk=1;Huffmanhf;//类对象
while(k)
{//将小写转化为大写}
switch(c)
{case'I':
//进入初始化选择界面
{
//选择初始化方式后,进入子菜单
switch(c){
case‘1’:
{hf.Initialization(HT,HC,current_n);break;}//手动初始化
case‘2’:
{//输入需要初始化的文件名(需包含后缀名.txt)建立哈夫曼树;
//建立哈夫曼树,并把哈夫曼树存放在ReadhfmTree.txt中
hf.Initialization(HT,HC,buf,current_n);break;
//从文件读入数据初始化
}
case‘3’:
{hf.Initialization(HT,HC,current_n,0);//标准初始化}
case‘4’:
break;
}
break;
}
case'E':
//进入编码选择界面
{//选择字符序列读入方式后进入子菜单
switch(c){
case'1':
{hf.EnCoding(HC,hufnum);break;//手动编码}
case'2':
{//输入需要的文件名(需包含后缀名.txt)进行编码
hf.EnCoding(HC,buf);break;//文件读入编码
}
case'3':
break;
}
break;
}
case'P':
//进入打印二进制编码界面
{hf.Print("ReadCodeFile.txt");break;}
case'T':
//进入打印哈夫曼树界面
{hf.Treeprinting(HT[2*current_n-1],HT,current_n);break;}
case'Q':
exit(-1);//退出
default:
exit(-1);
}
}
}
三、系统调试与测试
1、调试过程中遇到的问题及解决办法:
(1)逐个手动输入字符和权值进行编码,若数据太大效率太低。
解决办法:
后来增加一个新的功能从文本中读入数据,这样可大大提高效率。
(2)初始化文本读入时,若数据过大,会结束进程,无法进行操作。
解决办法:
增加动态数组的最大上限,当超过上限,会提示“文本数据过大”,而且可以显示范围内的数据。
(3)只能读取固定的文件进行编码。
解决办法:
可以手动输入想要读取的文件名。
(4)只能打印默认的哈夫曼树
解决办法:
通过增加两种初始化方式(手动初始化和文本读入初始化),打印用户当前初始化的哈夫曼树。
(5)进入子菜单后,输入的选项必须为数字,否则会出现死循环。
解决办法:
把输入的数据类型由整型改为字符型。
2、测试数据及其输出结果:
(1)进入主菜单界面,用户可以选择所要执行的操作,比如:
初始化<建立哈夫曼树>,编码,译码,打印二进制编码代码,打印哈夫曼树。
在执行编码、译码操作前,请先初始化默认的哈夫曼树(如图4所示)。
图4:
主菜单界面
(2)进入初始化界面,用户可以选择执行手动初始化(如图5所示),初始化结果存入WritehfmCode.txt,WritehfmTree.txt ;文本读入初始化(如图6所示),初始化结果存入ReadhfmCode.txt,ReadhfmTree.txt;默认文本初始化(如图7所示),初始化结果存入hfmCode.txt,hfmTree.txt。
图5:
手动初始化哈夫曼树
图6:
文本读入初始化哈夫曼树
图7:
默认文本初始化
(3)进入编码界面,用户可以选择执行手动编码(如图8所示),编码结果存入WriteCodeFile.txt;文本读入编码(如图9所示),编码结果存入ReadCodeFile.txt。
图8:
手动编码
图9:
文本读入编码
(5)进入打印编码代码界面(如图12所示),打印结果存入CodePrint.txt。
图12:
打印编码代码
(6)进入打印哈夫曼树,打印结果存入TreePrint.txt。
打印默认哈夫曼树(如图13所示),打印频度差距大的哈夫曼树(如图14所示),打印频度差距小的哈夫曼树(如图15所示)
图13:
打印默认哈夫曼树
图14:
打印频度差距大的哈夫曼树
图15:
打印频度差距小的哈夫曼树
四、结果分析
1、算法的时空分析和改进设想(选取主要函数)
(1)程序算法分析:
经过对程序中哈夫曼树的基本操作函数及其他相关算法的时空间复杂度的分析可知本程序中哈夫曼树的基本操作函数及相关算法的空间复杂度良好,但哈夫曼树的Initialization以及DeCoding操作函数的时间复杂度比较复杂,不过从总体的算法效率看,哈夫曼树的基本操作函数及其他相关算法时间及空间复杂度良好,总体效率良好。
(2)主要函数时空分析(n代表字符种类数)(如表2所示):
表2:
主要函数时空分析表
基本操作
时间复杂度分析
空间复杂度分析
Initialization
O(n*n)
S(n)
EnCoding
O(n)
S(n)
O(n)
S(n)
DeCoding
O(n*n)
S(n)
O(n*n)
S(n)
Print
O(n)
S(n)
Treeprinting
O(n)
S(n)
(3)改进设想:
1当前使用的是一维动态数组存储,当哈夫曼函数添加增加、删除、修改这些功能时,可选用链式存储哈夫曼树,效率会更高。
2、当前程序只能识别大写英文字母和空格,可改进为输入小写字母时也可识别。
3、当前程序是在先序遍历哈夫曼树时,采用递归算法,可以设计一个非递归算法遍历哈夫曼树,这样可以降低时间复杂度,提高程序运行速率。
2、经验和体会
一周的课程设计结束了,在这次的课程设计中不仅检验了我们所学习的知识,也培养了我们如何去把握一件事情,如何去做一件事情,又如何完成一件事情。
在设计过程中,与组员分工设计,相互探讨,相互学习,相互监督,学会了合作,学会了运筹帷幄,学会了宽容,学会了理解,也学会了做人与处世。
完成这次的课程设计任务,我们要做好以下准备:
(1)首先要熟练掌握二叉树的性质、先序遍历二叉树、最优二叉树的构建、字符串匹配等,然后在此基础上掌握理解huffman树和编码和译码。
(2)完成哈夫曼编译器,我们要考虑如何把文件当中的英文字母编成二进制代码,如何将二进制代码翻译成英文字母以及如何构建一棵哈夫曼树。
在这次的课程设计任务中,我们遇到的问题和困难:
(1)起初功能太简单,经过讨论,我们增加了一些必要的选择功能,比如:
读入方式分为文本读入和手动读入。
(2)逐个手动输入字符和权值进行编码,若数据太大效率太低。
后来增加一个新的功能从文本中读入数据,这样可大大提高效率。
(3)初始化文本读入时,若数据过大,会结束进程,无法进行操作。
后来增加动态数组的最大上限,当超过上限,会提示“文本数据过大”,而且可以显示范围内的数据。
每次出现问题我们都一起讨论,研究解决和改进的方法。
这次课程设计的成功,可以说是我们五个人一起努力的成果。
我们小组由五个人组成,每个人都有自己在小组中的作用,黄志发:
编写代码,设计界面,调试程序/施鸿俊、赖玉丹:
测试数据/邱琳娜、胡明丽:
文档编写和整理。
我们总是在不断地调试程序和改进程序的功能,皇天不负有心人,我们终于在自己的努力和老师的辛勤指导下顺利完成了课程设计。
五、参考文献
1《数据结构》(c++语言描述),殷人昆主编,清华大学出版社
2《数据结构题集》,严蔚敏编著,清华大学出版社
六、附录
1、源程序文件:
Huffman.h:
包含哈夫曼树结构体、哈夫曼编码结构体、结点结构体,哈夫曼树的类定义
Huffman.cpp:
哈夫曼树类的成员函数实现
main.cpp:
程序的入口,包含主界面和各个功能函数的调用
2、输入数据文本:
News.txt:
存储了一篇较大规模的大写英文文章,用于文件读入编码和文件读入初始化
123.txt:
存储了较小规模的大写英文文章,用于文件读入编码和文件读入初始化
321.txt:
存储了较小规模的0,1代码,用于文件读入进行译码
3、小组成员及分工表(如表3所示):