c++数据结构实验哈夫曼树.docx
- 文档编号:9487818
- 上传时间:2023-02-04
- 格式:DOCX
- 页数:22
- 大小:257.53KB
c++数据结构实验哈夫曼树.docx
《c++数据结构实验哈夫曼树.docx》由会员分享,可在线阅读,更多相关《c++数据结构实验哈夫曼树.docx(22页珍藏版)》请在冰豆网上搜索。
c++数据结构实验哈夫曼树
数据结构实验报告
1.实验要求
i.实验目的:
(1)掌握二叉树基本操作的实现方法
(2)掌握二叉树基本操作的实现方法
(3)了解哈夫曼树的思想和相关概念
(4)学习使用二叉树解决实际问题的能力
(5)熟悉C++语言的基本编程方法,掌握集成编译环境的调试方法,熟练改错方法。
(6)熟悉设计算法的过程
(7)进一步掌握指针、异常处理的使用
ii.实验容:
利用二叉树结构实现赫夫曼编/解码器。
基本要求:
1、初始化(Init):
能够对输入的任意长度的字符串s进行统计,统计每个字符的频度,并建立赫夫曼树
2、建立编码表(CreateTable):
利用已经建好的赫夫曼树进行编码,并将每个字符的编码输出。
3、编码(Encoding):
根据编码表对输入的字符串进行编码,并将编码后的字符串输出。
4、译码(Decoding):
利用已经建好的赫夫曼树对编码后的字符串进行译码,并输出译码结果。
5、打印(Print):
以直观的方式打印赫夫曼树(选作)
6、计算输入的字符串编码前和编码后的长度,并进行分析,讨论赫夫曼编码的压缩效果。
测试数据:
IlovedataStructure,IloveComputer.Iwilltrymybesttostudydatastructure.
提示:
1、用户界面可以设计为“菜单”方式:
能够进行交互。
2、根据输入的字符串中每个字符出现的次数统计频度,对没有出现的
字符一律不用编码。
iii.代码要求:
1、必须要有异常处理,比如删除空链表时需要抛出异常;
2、保持良好的编程的风格:
代码段与段之间要有空行和缩近
标识符名称应该与其代表的意义一致
函数名之前应该添加注释说明该函数的功能
关键代码应说明其功能
3、递归程序注意调用的过程,防止栈溢出
2.程序分析
树形结构是一种非线性结构可以用结点之间的分支来表示层次关系,二叉树是每个结点最多两个子树的有序树,十分适合计算机处理问题,而哈夫曼树是一种特殊的二叉树,它将权值大的数据放在了离根较近的结点处,这样使得带权路径长度最短,是非常好的存储方式。
2.1存储结构
1.结点结构的存储方式:
根(下面结点的父结点)结点:
左孩子
右孩子
……
structhnode//哈夫曼树结点的结构体
{
intweight;
intparent;
intlchild;
intrchild;
chardata;
};
结点存储示意图:
intweight
intparent
intlchild
intrchild
chardata
2.编码表的实现中使用了以下结构体:
Structhcode//编码表结构体
{
chardata;//字符
charcode[100];//编码容
};
示意图为:
chardata
charcode[100]
3.在select函数中使用的结构体:
structnode
{
intnum;
chardata;
};
intnum
chardata
2.2关键算法分析:
A)Init初始化:
统计需要编码的字符串中每个字符的频度并建立哈夫曼树
实现:
在函数中设置了一个数组type用来统计字符串中字符的类型,no数组则用于统计每种字符串的个数,count用于存储每类字符的相应的个数。
voidHuffman:
:
Init()//将输入的数据保存至类中
{
cout<<"请输入需要编译压缩的容"< cin.getline(in,500,'\n'); n=0; no=0; count=newnode[127];//type for(intj=0;j<127;j++)//对每一种字符的个数进行初始化 { count[j].num=0; } while(in[no]! ='\0')//结束之前,每输入一个字符,则对应的数目增1 { ++count[in[no]].num; count[in[no]].data=in[no]; ++no; } for(intk=0;k<127;k++){ if(count[k].num>0) { n++; cout< } } } 将初始化的数据用于建立哈夫曼树: voidHuffman: : createht() { no=0; htree=newhnode[2*n-1];//含有n种字符的哈夫曼树需要2*n-1个结点 for(inti=0;i { while(count[no].num==0)//该字符没有出现,跳过,继续找出现过的字符 { no++; } htree[i].weight=count[no].num;//将count里统计的次数传入哈夫曼树的节点中,作为字符权重 htree[i].lchild=-1; htree[i].rchild=-1; htree[i].parent=-1;//将左右孩子结点和父节点都置空 htree[i].data=count[no].data;//将字符传入哈夫曼树的结点 no++; } intx=-1,y=-1; for(inti=n;i<2*n-1;i++) { SelectMin(x,y,i);//挑选三者中的权重较小的两个 htree[x].parent=htree[y].parent=i;//令较小的x、y为孩子节点,该两个结点的父节点是i htree[i].weight=htree[x].weight+htree[y].weight;//i结点字符的权重赋为是左右孩子字符权重之和 htree[i].lchild=x;//左孩子为x htree[i].rchild=y;//右孩子为y htree[i].parent=-1;//父节点置空 x=-1; y=-1; } } 注意select函数的编写十分重要,必须成功选出每次权值最小的两个数据才能正确的建立哈夫曼树 voidHuffman: : SelectMin(int&x,int&y,intk)//选出权值较小的两个字符结点 { inti=0; while(i { while(i { if(x==-1)//左孩子 x=i; elseif(y==-1) y=i; else if(htree[x].weight<=htree[y].weight) { if(htree[y].weight<=htree[i].weight) { y=y;x=x; } else y=i; } else if(htree[x].weight>htree[y].weight) { if(htree[i].weight>=htree[x].weight) { x=x;y=y; } else x=i; } i++; } i++; } } B)createtable建立编码表: 利用初始化得到的结果将哈夫曼树进行编码并输出每个字符的编码。 1.在程序中设置了一个数组save来存储每个字符的编码。 voidHuffman: : createhc()//建立哈夫曼编码表 { hcodetable=newhcode[n];//生成编码表 for(inti=0;i { hcodetable[i].data=htree[i].data; intchild=i; intparent=htree[i].parent; intk=0; while(parent! =-1) { if(child==htree[parent].lchild)//该节点是父节点的左孩子则编码为0,右孩子则编码为1 hcodetable[i].code[k]='0'; else hcodetable[i].code[k]='1'; k++; child=parent;//将该节点的父节点进行编码输出 parent=htree[child].parent; } hcodetable[i].code[k]='\0';//code数组以\0结尾 Reverse(hcodetable[i].code);//逆置输出字符的编码值 } cout<<"每个字符的编码为: "< for(inti=0;i { cout< "< } } 2.输出编码的结果与创建顺序相反故需要建立一个数组将编码数组的顺序调整。 voidReverse(charuse[])//逆置函数 { chart[500]; inti=0,j=0; while(use[i]! ='\0') { t[i]=use[i]; i++; } t[i]='\0'; i--; while(i>=0)//通过t数组将use数组的数据逆序排序 { use[j]=t[i]; i--; j++; } use[j]='\0'; } C).Encoding 根据编码表对输入的字符串进行编码,并将编码后的字符串进行输出。 voidHuffman: : Encoding()//编译输入容为代码容用0和1表示 { cout<<"编码结果为: "; intk=0; for(inti=0;in[i]! ='\0';i++) { intj=0; while(hcodetable[j].data! =in[i])//编码表的字符等于输入容的字符时进行下一个while循环 { j++; } intm=0; while(hcodetable[j].code[m]! ='\0')//输出该字符的编码 { save[k]=hcodetable[j].code[m];//save数组记录编码数据 cout< k++; m++; } } save[k]='\0'; cout< } D).Decoding译码: 利用建好的哈夫曼树对编码后的字符串进行译码,并输出译码的结果。 voidHuffman: : Decoding() { cout<<"解码结果为: "; inti=0,j=0; while(save[i]! ='\0') { intchild=2*n-1-1;//找到保存根节点的数组数 while(htree[child].lchild! =-1)//若是这个节点父节点的孩子存在则继续向下寻找 { if(save[i]=='0') child=htree[child].lchild;//是0则找左孩子 else child=htree[child].rchild;//是1则找右孩子 i++; } c[j]=hcodetable[child].data;//若不存在孩子节点,则令数组的元素为该节点的字符变量 cout< j++;//逐个输出字符容 } } E).print打印: 以直观的方式打印哈夫曼树 通过递归调用从根结点开始以中序遍历的方式打印出树 voidHuffman: : printtree(intqi,intf) { if(htree[qi].lchild! =-1) printtree(htree[qi].lchild,f+4); cout< if(qi cout< else cout< if(htree[qi].rchild! =-1) printtree(htree[qi].rchild,f+4); } F).计算输入输出前后编码长度并进行分析讨论压缩效果 intHuffman: : getsizea() { returnstrlen(in); } intHuffman: : getsizeb() { returnstrlen(save); } (b-a)即为所求。 2.3其他 1.哈夫曼树的类: classHuffman { private: hnode*htree;//哈夫曼树 hcode*hcodetable;//哈夫曼编码表 charc[127];//字符类型 node*count;//记录各个字符及其出现次数 intn;//输入字符的种类的个数 intno;//输入字符的个数 charin[500];//保存输入的字符串 charsave[500];//记录所有输入容被编码后的结果 public: voidcreateht();//创建哈夫曼树 voidcreatehc();//创建编码表 voidInit();//初始化 voidEncoding();//编码 voidDecoding();//解码 voidSelectMin(int&x,int&y,intk);//求最小权重的字符 intgetsizea(); intgetsizeb(); voidprinttree(intqi,intf);//打印哈夫曼树; intgetn(); ~Huffman(); }; 2.switch语句: switch(choice) { case1: ht.Init(); break; case2: ht.Encoding(); break; case3: ht.Decoding(); break; case4: ht.printtree(2*qi-1-1,f); break; case5: cout<<"压缩前"< cout<<"压缩了"<<(b-a)<<"个bit"< break; default: break; } 3.计算时间复杂度: A)o(n); B)o(n); C)o(n); D)o(n); E)o(n); F)o (1); 3.程序运行结果 1.程序流程图: 否 是 2.测试条件: 输入字符串的长度小于500; 3.测试结论: 4.总结 在这次实验中我通过不断的调试,理解了树的结构与哈夫曼树编解码的代码,并且回忆了上学期c++中学习到的知识,编写了select和reverse函数,在编程过程中出了许多错误: 1.是在switch语句中赋值,经过程序提示改为了在主函数中赋值 voidmain() { Huffmanht; ht.Init(); ht.createht(); ht.createhc(); ht.Encoding(); ht.Decoding(); intf=0; intqi=ht.getn(); inta=ht.getsizea(); intb=ht.getsizeb(); menu(); intchoice=1; for(intp=0;p<4;p++) { cin>>choice; switch(choice) { case1: ht.Init(); break; case2: ht.Encoding(); break; case3: ht.Decoding(); break; case4: ht.printtree(2*qi-1-1,f); break; case5: cout<<"压缩前"< cout<<"压缩了"<<(b-a)<<"个bit"< break; } } } 2.因为粗心还直接用主函数中的函数直接调用类的私有成员,这样的错误实在不应该,后面通过类对象调用类的共有函数而实现了对数据成员的操作。 3.Printtree的过程比较需要使用递归函数,在调试中回忆了相应的知识。 整体而言,虽然花费了大量的时间,但是收获也很丰富,程序比较长下一步需要化简处理,使代码更为灵活生动
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- c+ 数据结构 实验 哈夫曼树