《用哈夫曼编码实现文件压缩》实验报告课程名称数据结构实验学期2013至2014学年第2学期学生所在系部计算机学院年级2012级专业班级计算机科学与技术学生姓名学号任课教师实验成绩一、实验题目哈夫曼编码实现文件压缩二、实验目的:1、了解文件的概念。2、掌握线性链表的插入、删除等算法。3、掌握Huffman树的概念及构造方法。4、掌握二叉树的存储结构及遍历算法。5、利用Huffman树及Huffman编码,掌握实现文件压缩的一般原理。三、实验设备与环境:微型计算机、Windows系列操作系统、VisualC++6.0软件。四、实验内容:根据ASCII码文件中各ASCII字符出现的频率情况创建Haffman树,再将各字符对应的哈夫曼编码写入文件中,实现文件压缩。五、概要设计:本次实验采用将字符用长度尽可能短的二进制数位表示的方法,即对于文件中出现的字符,无须全部都用8位的ASCII码进行存储,根据他们在文件中出现的频率不同,我们利用Haffman算法使每个字符能以最短的二进制字符进行存储,以达到节省存储空间,压缩文件的目的。解决了压缩需采用的算法,程序的思路已然清晰:1.统计需压缩文件中每个字符出现的频率。2.将每个字符的出现频率作为叶子结点构建Haffman树,然后将树中结点引向其左孩子的分支标“0”,引向其右孩子的分支标“1”;每个字符的编码即为从根到每个叶子的路径上得到的0、1序列,这样便完成了Haffman编码,将每个字符用最短的二进制字符表示。3.打开需压缩文件,再将需压缩文件中的每个ASCII码对应的Haffman编码按bit单位输出。4.文件压缩结束。六、详细设计:(1)构造Hufffman树的方法—Hafffman算法构造Huffman树步骤:I.根据给定的n个权值{w1,w2,⋯⋯wn},构造n棵只有根结点的二叉树,令起权值为wj。II.在森林中选取两棵根结点权值最小的树作左右子树,构造一棵新的二叉树,置新二叉树根结点权值为其左右子树根结点权值之和。III.在森林中删除这两棵树,同时将新得到的二叉树加入森林中。Ⅳ.重复上述两步,直到只含一棵树为止,这棵树即哈夫曼树。对于Haffman的创建算法,有以下几点说明:a)这里的Haffman树采用的是基于数组的带左右儿子结点及父结点下标作为存储结点的二叉树形式,这种空间上的消耗带来了算法实现上的便捷。b)由于对于最后生成的Haffman树,其所有叶子结点均为从一个内部树扩充出去的,所以,当外部叶子结点数为m个时,内部结点数为m-1,整个Haffman树的需要的结点数为2m-1c)初始化Hafffman树分两步进行,先将所有结点赋值,再将前m个叶子结点赋初值。d)在查找权值最小并且父结点为空的两个结点时,通过逐个比较,将两结点的位置下标与权值分别保存。方便在与其父结点建立联系时调用。2)压缩过程的实现:压缩过程的流程是清晰而简单的:1创建Haffman树→2打开需压缩文件→3将需压缩文件中的每个ASCII码对应的Haffman编码按bit单位输出→4文件压缩结束。其中,步骤1和步骤3是压缩过程的关键。开始定义Hafffman树初始化Hafffman树i=0im-1Hafffman创建完毕i++将下标为m+i的结点作为所找出的两结点的父结点,建立联系在前m+i个结点中找出权值最小并且父结点为空的两结点a)步骤1:这里所要做工作是得到Haffman数中各叶子结点字符出现的频率并进行创建。b)步骤3:将需压缩文件中的每个ASCII码对应的Haffman编码按bit单位输出,这是本压缩程序中最关键的部分。这里涉及“转换”和“输出”两个关键步骤:“转换”部分大可不必去通过遍历Haffman树来找到每个字符对应的哈夫曼编码,可以将每个码值及其对应的ASCII码存放于如下所示的结构体中:typedefstruct{charasciiCode;unsignedlonghaffCode;inthaffCodeLen;}HaffCode;创建由该结构体结点所组成的,长度为128的一维数组codeList[128],且codeList中的下标和asciiCode满足下面的顺序存放关系:codeList[i].asciiCode=i;这样的话,查找某个字符inChar的Haffman编码的工作便变得相当轻松了,如下:sHaffCode=codeList[inChar].haffCode;数组codeList[128]的创建可以采用某种遍历方式下的按找到的字符进行置数的方式,十分的方便。以下流程图采用的是前序遍历的方式创建:说明:1.在流程图中,youBiao中存放字符对应的Haffman编码,sDepth中存放其Haffman编码的长度。2.在代码的编写过程中,可用递归调用实现。NYYNYNYNNY(3)“输出”部分是最重要的部分,也是最易出错的部分。每个字符要能合理的结束。这主要是为解压缩考虑的,比如在最后,这里涉及到C语言的位操作,要求这个算法能处理好以下几个问题:1)每个字符所对应的haffCode的比特位长度由5~23位不等长,不可少输,多输,输错任何一位,后一个字符的haffCode要紧跟在前一个字符的haffCode后面。youBiao1丨0x01,sDepth++;结点指向其右结点判断当前结点是否有孩子结点当前结点是否有左孩子结点点当前结点是否有右孩子结点当前结点是否有父结点当前结点的左右孩子是否都已访问YouBiao1,sDepth++向其左结点youBiao,sDepth与结点返回其父结点youBiao为Haffman编码值即开始文件的压缩编码成功2)最后一个要输出的haffCode的最后一位,它恰好是位于最后一个有效字符的第一位,剩下的七个空位是要用无效的haffCode加以填充的。否则,如果填充的haffCode亦为某个ascii字符的haffCode时,那么在解压缩时,则该在原被压缩文件中不存在的字符便会无中生有的在解压后的文件中出现,这显然是不正确的,应在程序中加以处理。NYNYYN当文件不为空时count=0count8当前的一个字符对应的haffCode已输出完毕将curCode中的当前位赋值给输出字符左起的第count个位置输出字符到压缩文件读入被压缩文件的下一个字符,得到其haffCode,设为curCode,如是最后一个字符,则做相应y的处理Count++(4)main函数部分NYNY开始是否成功输入要打开文件名称是否找到key.txt文件将key文件中的值作为叶子结点构建haffman树实现haffman编码输出ASCII码对应的haffman编码及其长度输出字符到压缩文件文件压缩结束,关闭打开的文件返回七、测试结果及分析:运行结果:压缩情况:实验分析:数据结构将各个抽象的数据之间的关系建立起来,无论是线性的、循环的还是分支的,都是为了建立一种方便程序的实现和运行的结构,使得数据之间不再是孤立的。它能使得我们在编程时在脑海中显现更为清晰的数据关系画面。而且在学习数据结构时我们更应该联系所属语言(我们所学的是C语言版)的特性,这样才能更好的理解数据结构的思想体系。以上程序实验采用的方案:即统计了若干篇不同的文章中字符出现的频率。已事先统计每个字符出现的频率放在KEY.txt文件中,然后程序运行时自动将字符的权读出存放在一个以为数组中即:wList[i]中,通过实参传给形参*w,以此按先序遍历二叉树的方式构造Huffman树,得各字符的Huffman编码值.在进行压缩时候,程序界面会自动提醒输入要压缩的文件名其压缩的文件扩展名为*.zip.每个字符的存储编码与Huffman编码一一对应,可以达到无损压缩的目的,由于KEY.txt文件的存在可以为后续解压做准备。总的来说,这次实验带来的收获是很大的,提取文件数据、分析数据、构建Huffman树、替换数据对文件进行压缩、输出文件,一次大的实验几乎运用到了我们一学期所学的所有知识。经过分、析调试和了解程序的代码,巩固了上课学习的知识。在这次试验中,我感觉到了自己的不足之处,设计思路不够清晰,运行出现错误也不能很好的调整改正。在以后的学习生活中,应当努力提高自己,让自己的未来更加清晰明了。八、教师评语:教师评价评定项目ABCD评定项目ABCD算法正确界面美观,布局合理程序结构合理操作熟练语法、语义正确解析完整实验结果正确文字流畅报告规范题解正确其他:评价教师签名:年月日