哈夫曼编码实验报告_第1页
哈夫曼编码实验报告_第2页
哈夫曼编码实验报告_第3页
哈夫曼编码实验报告_第4页
哈夫曼编码实验报告_第5页
已阅读5页,还剩11页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、. 姓 名: 刘阳 班 级: 信息0703 学 号:0903070312 实验时间: 08.11.14 指导老师: 赵颖目 录一、实验内容2二、实验说明2三、结构体定义和程序结构的说明31.结构体定义的说明32.程序结构的说明3四、程序设计的基本思想、部分源代码及注释31.选择权值最小的两个结点4.判断结点是否已经被使用过4 .选出权值为最小的结点4 .选出另一个权值为最小的结点5 .判断两个选出的最小权值的大小52构建哈夫曼树6 .判断能否构建成哈夫曼树6 .对需要处理的结点和哈夫曼树的结点进行初始化6 .构建哈夫曼树6 .对构建好的哈夫曼树进行遍历确定每个结点的编码73输出设置7 .输出每

2、个结点的父亲、左孩子、右孩子结点7 .输出每个结点的哈夫曼编码8五、流程图六、实验结果演示七、在编写程序过程中遇到的困难和解决的方法一、实验内容根据输入的n个带权结点,构造出哈夫曼树,并且把构造结果输出到屏幕。二、实验说明哈夫曼数,也称最优二叉树,是指对于一组带有确定权值的叶结点,构造的具有最小带权路径长度的二叉树。设二叉树具有n个带权值的叶结点,那么从根结点到各个叶结点的路径长度与相应结点权值的乘积之和叫做二叉树的带权路径长度WPL,记作: WPL=。在给定一组具有确定权值的叶结点,可以构造出不同的带权二叉树。根据哈夫曼树的定义,一棵二叉树要使其WPL值最小,必须使权值越大的叶结点越靠近根结

3、点,而权值越小的叶结点越远离根结点。在数据通讯中,经常需要将传送的文字转换成由二进制字符0,1组成的二进制串,我们称之为编码。例如,假设要传送的电文为ABACCDA,电文中只含有A,B,C,D四种字符,若这四种字符采用下表所示的编码,则电文的代码为000010000100100111 000,长度为21。 在传送电文时,我们总是希望传送时间尽可能短,这就要求电文代码尽可能短。如果在编码时考虑字符出现的频率,让出现频率高的字符采用尽可能短的编码,出现频率低的字符采用稍长的编码,构造一种不等长编码,则电文的代码就可能更短。并且在建立不等长编码时,必须使任何一个字符的编码都不是另一个字符编码的前缀,

4、以避免反译成原文时,编码出现多义性。在哈夫曼编码树中,树的带权路径长度的含义是各个字符的码长与其出现次数的乘积之和,也就是电文的代码总长,所以采用哈夫曼树构造的编码是一种能使电文代码总长最短的不等长编码。采用哈夫曼树进行编码,也不会产生上述二义性问题。因为,在哈夫曼树中,每个字符结点都是叶结点,它们不可能在根结点到其它字符结点的路径上,所以一个字符的哈夫曼编码不可能是另一个字符的哈夫曼编码的前缀,从而保证了译码的非二义性。 三、结构体定义和程序结构的说明1、结构体定义的说明哈夫曼树重点在于如何排列权值大小不同的结点的顺序,所以定义结构体如下:typedef structint weight;

5、/*weight存储结点的权值*/int parent;int lchild;int rchild; /*分别保存父亲、左孩子、右孩子结点*/HTNode,*HuffmanTree;而另一个重点在于将两个权值为最小的结点分别作为左、右子树,所以定义结构体如下:typedef structunsigned int s1;unsigned int s2; /*分别存储最小和次小的结点*/MinCode;2、程序结构的说明程序主要由以下几部分组成:结构体 struct HTNode,*Huffmantree结构体 struct MinCode函数 Select 用以选择结点中权值最小的两个结点函数

6、CreateTree 将选出来的结点按规律逐步建成哈夫曼树 函数 main 主函数四、程序设计的基本思想、部分源代码及注释根据哈夫曼树的定义,一棵二叉树要使其WPL值最小,必须使权值越大的叶结点越靠近根结点,而权值越小的叶结点越远离根结点。因此,构造哈夫曼树有此种方法:1、由给定的n个权值W1,W2,Wn构造n棵只有一个叶结点的二叉树,从而得到一个二叉树的集合FT1,T2,Tn;2、在F中选取根结点的权值最小和次小的两棵二叉树作为左、右子树构造一棵新的二叉树,这棵新的二叉树根结点的权值为其左、右子树根结点权值之和;3、在集合F中删除作为左、右子树的两棵二叉树,并将新建立的二叉树加入到集合F中;

7、4、重复(2)(3)两步,当F中只剩下一棵二叉树时,这棵二叉树便是所要建立的哈夫曼树。所以,构造哈夫曼树主要由两个步骤组成:一是选择所有结点中权值最小的两个结点,二是将这些结点加入到二叉树中,构建成哈夫曼树。1、 在所有结点中选出权值最小的两个结点。、选择权值最小的两个结点并不难,难在如何判断该结点是否已经使用过,若不能正确判断前面构造的哈夫曼树中是否使用过该结点,可能造成结点重复出现在树中,出现错误。根据哈夫曼树构造的特点,当两个结点的权值为最小时就将做为新的二叉树的左(右)子树,而它们的权值之和为它们的根结点,所以可以通过判断该结点是否有父亲结点来判断它是否被使用过。if(HTi.pare

8、nt=0) /*没有父亲结点说明该 结点还未被使用过*/ min=HTi.weight; /*给min赋初始值*/ s1=i; /*将结点的编号赋给s1*/ break; tempi=i+; /*i确定下一个结点的编号*/、然后利用数字排序的方法,对符合要求的结点进行判断,当存在权值更小的结点是,将该结点的内容赋给min和s1.for(;i=n;i+) if(HTi.weightmin&HTi.parent=0) /*当结点i未被使用并且权min=HTi.weight; 值小于min时,将权值赋s1=i; 给min,编号赋给s1*/ 、采用同样的方法可以选择出另一个权值为最小的结点。for(i

9、=tempi;i=n;i+) if(HTi.parent=0&i!=s1) secmin=HTi.weight; s2=i; break; for(i=1;i=n;i+) if(HTi.weights2) temp=s1; s1=s2; s2=temp; code.s1=s1; code.s2=s2; return code;2、 将选择出来的结点一个个逐步构建成哈夫曼树。、哈夫曼树实现的功能就是将若干个结点以最优化的顺序排列出来,所以当结点数n=1时,不存在构建哈夫曼树的问题。因此,首先对可能出现的这种状况进行判断。if(n=1) printf(Error:Code too small!);

10、 、因为哈夫曼树的叶子结点为n个,结点数为2*n-1个,所以可以直接定义构建的哈夫曼树的结点个数m。并对输入的结点和待构建的哈夫曼树上的结点进行初始化。m=2*n-1; /*定义哈夫曼树的结点个数*/HT=(HuffmanTree)malloc(m+1)*sizeof(HTNode); for(p=HT,i=0;iweight=*w; p-parent=0; /*给待处理的结点赋予权值,父 p-lchild=0; 亲、左孩子、右孩子均赋0值*/ p-rchild=0; for(;iweight=0; p-parent=0; /*对待构建的哈夫曼树 p-lchild=0; 的编码进行初始化*/

11、p-rchild=0; 、构建哈夫曼树,通过Select函数选择还未被使用的且权值为最小的两个结点,将其加入到树中。for(i=n+1;i=m;i+) min=Select(HT,i-1); s1=min.s1; /*s1、s2分别存权值最小 s2=min.s2; 的两个结点的编号*/HTs1.parent=i; HTs2.parent=i; HTi.lchild=s1; HTi.rchild=s2; HTi.weight=HTs1.weight+HTs2.weight; 、构建完成哈夫曼树后,为知道每个结点的具体编码,必须对哈夫曼树进行遍历,且必须从叶子结点向根结点逆序进行遍历。HC=(Hu

12、ffmanCode)malloc(n+1)*sizeof(char *); cd=(char *)malloc(n*sizeof(char *); /*分配求编码的工作空间*/cdn-1=0; /*编码结束符*/for(i=1;i=n;i+) /*逐个字符求赫夫曼编码*/ start=n-1; /*编码结束符位置*/ for(c=i,f=HTi.parent;f!=0;c=f,f=HTf.parent) /*从叶子到根逆向求每个 if(HTf.lchild=c) 字符的赫夫曼编码*/ cd-start=0; /*左孩子赋0值*/ else cd-start=1; /*右孩子赋1值*/ 3、输出

13、的设置 由于哈夫曼树的树形结构比较难在计算机屏幕上实现,而又需要明确的表示出每个结点在树中的位置,所以对输出采取了以下两种样式。 、输出每个结点的父亲、左孩子、右孩子结点的编号,从而确定每个结点的具体位置。由于在构建哈夫曼树的Create函数中已经完成了每个结点的父亲、左孩子、右孩子结点的赋值,所以只需要将这些值直接输出即可。printf(HT List:n); printf(Numberttweightttparentttlchildttrchildn); for(i=1;i=m;i+) printf(%dtt%dtt%dtt%dtt%dn, i,HTi.weight,HTi.parent,

14、HTi.lchild,HTi.rchild); 、哈夫曼树的功能是将每个结点用0和1表示出来,所以将每个结点的哈夫曼编码表示出来也可以确定哈夫曼树的结构。而在Create函数中,同样已经给确定了结点的0值和1值,只需要调用一个字符串函数将每个结点的编码复制到该结点相应的编码值Code中。然后再在主函数中将其打印出来。HCi=(char *)malloc(n-start)*sizeof(char *); /*为第i个字符编码分配空间*/strcpy(HCi,&cdstart); /*从cd复制编码(串)到HC*/printf(HuffmanCode:n); printf(NumberttWeig

15、htttCoden); for(i=1;i=n;i+) printf(%dtt%dtt%sn,i,wi,HCi); 五、流程图main函数: Select函数: CreatTree函数: 六、实验结果演示1、当输入的结点个数n2时:2、当输入的结点数n=1时,不存在构建哈夫曼树的问题,输出错误提示:七、在编写程序过程中遇到的困难和解决的方法数据结构上机实验是使我们进一步掌握和深化所学知识的必不可少的内容之一,是后继专业课程的基础,是培养我们综合能力和提高我们科学素养的必不可少的部分,所以我对每次实验都抱以极其认真的态度。在刚开始拿到程序的题目时,不知道从何入手,因为题目表现的比较抽象,并且自己

16、在写哈夫曼树的过程中都遇到一些困难,所以感觉很难完成。百般无奈之下,回归课件,将哈夫曼树那一节仔细的看了几遍才看懂了编写哈夫曼树的基本思想。程序的重点之一是选择权值最小的两个结点,最开始将问题考虑的太过简单,使用数字排序的冒泡法之类的就能找到最小的两个结点,然后使用完这两个结点后将它们删除掉就可以了。但是在实际操作中,结点的内容是使用数组的形式存储下来的,而数组最麻烦的操作就是在数组中进行插入和删除结点。当时怎么也想不到解决的办法。后来在翻阅C语言的书的时候,看到在判断一个数是否为素数的程序中使用了flag标志,考虑先将所有的结点的flag标志都赋为0,而使用完某个结点后就将它的flag标志改

17、为1,这样就不会出现结点被重复使用的现象了。后来在网上查找类似的程序中发现了一个更好的办法,就是运用结点插入后的本来性质,因为在将结点加入到哈夫曼树中,它一定会有父亲结点,所以先将需要处理的结点的父亲结点全赋予0值,而加入到树中后有了父亲结点,则父亲结点不再为0,可以通过判断parent=0是否成立来判断结点是否被使用过。这样在很大程度下简化了程序。在构建哈夫曼树的过程中,想到每两个最小权值结点的父亲结点是需要另外存储的,而最开始虽然按照课件上的内容将哈夫曼树的结点数设置成2*n-1个,却不知道如何使用。最初的想法是在选择出两个结点后将它们重新编号,从1开始,然后自然而然它们的父亲结点就为3,

18、依此类推。同样的也是在实际操作中,发现这样做真是自己给自己找麻烦,不仅容易将原来需要处理的结点搞混,重新编号也是一项大工程。后来在同学的帮助下,发现自己思考的太复杂了,可以直接将第一个父亲结点的编号设置为n+1即可,因为只有n个结点需要处理,后面的m-n个结点都是父亲结点。最后一个问题是输出的设置。因为是要构建哈夫曼树,所以理所当然的认为要输出一个树的结构,但是很明显根本不知道要如何实现这一类的操作,所以又一次陷入僵局。在同学的提醒下,改换了思维,只要能够表示出树的结构就可以了,并不一定要将一颗树给画出来。而表现出哈夫曼树的结构可以通过它的编码来显示,也可以通过输出它的父亲结点和孩子结点来表示。好在这两种

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论