哈夫曼压缩解压-大数据结构设计报告材料

合集下载

数据结构哈夫曼压缩软件设计实验报告

数据结构哈夫曼压缩软件设计实验报告

东北大学信息科学与工程学院数据结构课程设计报告题目哈夫曼压缩软件设计课题组长王健课题组成员张颖刘琪张晓雨专业名称计算机科学与技术班级计1307指导教师杨雷2015 年1月课程设计任务书目录1 课题概述 (4)1.1 课题任务 (4)1.2 课题原理 (4)1.3 相关知识 (4)2 需求分析 (5)2.1 课题调研 (5)2.2 用户需求分析 (5)3 方案设计 (5)3.1 总体功能设计 (5)3.2 数据结构设计 (6)3.3 函数原型设计 (6)3.4 主算法设计 (7)3.5 用户界面设计 (9)4 方案实现 (12)4.1 开发环境与工具 (12)4.2 程序设计关键技术 (12)4.3 个人设计实现(按组员分工)4.3.1 王健设计实现 (12)4.3.2 张颖设计实现 (17)4.3.3 刘琪设计实现 (20)4.3.4 张晓雨设计实现 (22)5 测试与调试 (25)5.1 个人测试(按组员分工) (25)5.1.1 王健测试 (25)5.1.2 张颖测试 (26)5.1.3 刘琪测试 (27)5.1.4 张晓雨测试 (31)5.2 组装与系统测试 (32)5.3 系统运行 (32)6 课题总结 (33)6.1 课题评价 (33)6.2 团队协作 (33)6.3 下一步工作 (33)6.4 个人设计小结(按组员分工) (33)6.4.1 王健设计小结 (33)6.4.2 张颖设计小结 (34)6.4.3 刘琪设计小结 (34)6.4.4 张晓雨设计小结 (34)7 附录A 课题任务分工 (35)A-1 课题程序设计分工 (35)A-2 课题报告分工 (36)附录B 课题设计文档(光盘) (37)B-1源程序代码(*.H,*.CPP) (37)B-2工程与可执行文件) (37)附录C 用户操作手册(可选) (37)C.1 运行环境说明 (37)C.2 操作说明 (37)1 课题概述1.1课题任务采用哈夫曼树求得的用于通信的二进制编码称为哈夫曼编码。

数据结构课程设计_哈夫曼压缩文件

数据结构课程设计_哈夫曼压缩文件

《数据结构》基于哈夫曼算法的文件压缩程一.总体设计1.目标设计:*实现目标:利用哈夫曼算法编写一个可以对文件进行压缩和解压缩的程序,即可以将指定的文件用哈夫曼算法压缩为一个新的文件,也可以将一个压缩后的文件还原,并可以将压缩或还原后的文件保存到指定位置。

*功能描述:任何文件都可以看作是由字节组成的字节块,将字节看作基本编码单元,一个文件就可以看作是由字节组成的信息串。

对文件中各字节的出现频率进行统计,并以出现频率作为字节的权值,就可以用字节为叶结点构造哈夫曼树,进而构造出各字节的对应哈夫曼编码。

字节编码是一种8位定长编码,将各字节用哈夫曼编码进行重新编码,就有可能使得总的编码长度更短,从而达到压缩的效果。

哈夫曼编码是无损压缩当中最好的方法。

它使用预先二进制描述来替换每个符号,长度由特殊符号出现的频率决定。

常见的符号需要很少的位来表示,而不常见的符号需要很多位来表示。

哈夫曼算法对文件的压缩和解压缩的程序就是将存储源文件的二进制编码通过利用哈夫曼算法译为长度不等的哈夫曼编码,即得到哈夫曼树。

这棵树有两个目的:1.编码器使用这棵树来找到每个符号最优的表示方法,进而存储树实现文件的压缩。

2.解码器使用这棵树唯一的标识在压缩流中每个编码的开始和结束,其通过在读压缩数据位的时候自顶向底的遍历树,选择基于数据流中的每个独立位的分支,一旦一个到达叶子节点,解码器知道一个完整的编码已经读出来了,即通过对哈夫曼树的遍历实现解压过程。

2.框架设计:定义结构体类型变量struct head {}定义函数void compress() /*压缩文件*/{在函数compress内定义变量;读取被压缩文件;建立并打开目标文件;逐字节读入,并进行累加计数,得到各个字节在文件中的出现频率;利用哈夫曼算法构造出字节对应的哈夫曼树;将压缩后的数据写入目标文件,并保存;}定义函数uncompress() /*解压文件*/{在函数uncompress内定义变量;读取需解压文件;建立并打开目标文件;对哈夫曼树进行遍历实现解压;将解压后的数据写入目标文件,并保存;}定义主函数int main(){输入A,压缩文件,调用函数compress;输入B,解压文件,调用函数uncompress;}二.详细设计1、文件的字节频率统计字节共有256个,从0~255,可定义长度为256的频率数组来记录每个字节的出现频率。

数据结构-哈夫曼编码压缩解压缩设计报告

数据结构-哈夫曼编码压缩解压缩设计报告

;} 数个的0的充填时个8满不tib的中etyb后最到缩压// ;munkcal tni 数个的tib中etyb// ;munstib tni 量变的tib冲缓来用时件文缩压// ;etyb rahc 1-2*fael过超会不数个 点结大最的树 �树namffuh示表来用 �组数的构结edonTH// ;]1-2*fael[TH edonTH 数个的符字同不录记// ;munfael tni 置位的点结根录记// ;toor tni :etavirp 较比件文的后复恢与件文原将// ;)tuptuo* rahc ,tupni* rahc(2erapmoc diov 较比件文的后缩压与件文原将// ;)tuptuo* rahc ,tupni* rahc (erapmoc diov eslaf败失 eurt 回返功成,数函复恢// ;)tuptuo* rahc ,tupni* rahc (sserpmoced loob eslaf 败失 eurt 回返功成,数函缩压// ;)tuptuo* rahc ,tupni* rahc(sserpmoc loob 空清etyb将// ;)(etybteser diov tib个一入加中etyb的tib个8满未个一对时缩压// ;)tib tni (tibdda diov 码编namffuh的符字个每出列// ;)(edoctnirp diov 码编namffuh的符字个每算计树namffuh用利时缩压// ;)(edoc diov
树namffuh 造构值权的点结各据根时缩压// ;)(etaerc diov 值权的点结应对入写其将�数次的现出符字各计统时缩压// ;)tupni* rahc(tnuoc loob ;)(eerTnamffuh~ lautriv ;)(eerTnamffuh :cilbup { eerTnamffuh ssalc ;edonTH} } ;0 = neledoc ;1- = dlihcr ;1- = dlihcl ;1- = tnerap ;XAM = thgiew { )(edonTH 子孩右左及亲双无�大穷无为值权其令�点结化始初// 度长的码编namffuh点结该录记// ;neledoc tni 码编namffuh的点结该录记// ;edoc* tni 子孩右的点结// ;dlihcr tni 子孩左的点结/ ;dlihcl tni 置位点结亲双的点结录记// ;tnerap tni 值权的点结录记// ;th giew gnol { edonTH tcurts fedepyt 体构结点结的树namffuh//

用哈夫曼编码实现文件压缩.doc

用哈夫曼编码实现文件压缩.doc

《用哈夫曼编码实现文件压缩》实验报告课程名称数据结构(B)实验学期2009 至2010 学年第 1 学期学生所在系部计算机系年级2007 专业班级计算机B071学生姓名陆永芳学号200707014105任课教师盛建瓴实验成绩用哈夫曼编码实现文件压缩1、了解文件的概念。

2、掌握线性链表的插入、删除等算法。

3、掌握Huffman树的概念及构造方法。

4、掌握二叉树的存储结构及遍历算法。

5、利用Huffman树及Huffman编码,掌握实现文件压缩的一般原理。

微型计算机、Windows 系列操作系统、Visual C++6.0软件根据ascii码文件中各ascii字符出现的频率情况创建Haffman树,再将各字符对应的哈夫曼编码写入文件中,实现文件压缩。

本次实验采用将字符用长度尽可能短的二进制数位表示方法,即对于文件中出现的字符,无须全部都用8位的ASCII码进行存储,根据他们在文件中出现色频率不同,我们利用Haffman算法使每个字符都能以最短的二进制字符进行存储,以达到节省存储空间,压缩文件的目的。

解决了压缩需采用的算法,程序的思路就清晰了:1.统计需压缩文件中每个字符出现的频率2.将每个字符的出现频率作为叶子结点构建Haffman树,然后将树中结点引向其左孩子的分支标“0”,引向其右孩子的分支标“1”;每个字符的编码即为从根到每个叶子的路径得到0、1的序列,这样便能完成了Haffman的编码,将每个字符用最短的二进制字符表示。

3.打开需压缩文件,再将需压缩文件中的每个ascii码对应的Haffman编码按bit单位输出。

4.文件压缩结束。

(1)构造Huffman树的方法——Haffman算法构造Huffman树步骤:I.根据给定的n个权值{w1,w2,……,wn},构造n棵只有根结点的二叉树,令起权值为wj。

II.在森林中选取两棵根结点权值最小的树作为左右子树,构造一棵新的二叉树,置新二叉树根结点权值为其左右子树根结点权值之和。

数据结构课程设计报告Huffman编码与文件压缩

数据结构课程设计报告Huffman编码与文件压缩

数据结构课程设计报告--Huffman编码与文件压缩课程设计报告题目:题目三哈夫曼编码与文件压缩课程名称:数据结构专业班级:计算机科学与技术1003班学号:姓名:鲁辰指导教师:报告日期:2012.09.26计算机科学与技术学院目录1任务书 (4)2 绪言 (5)2.1 课题背景 (5)2.2 课题研究的目的和意义 (5)2.3 国内外概况 (5)2.4 课题的主要研究工作 (5)3 系统设计方案的研究 (6)3.1 系统的控制特点与性能要求 (6)3.2 系统实现的原理 (6)3.2.1 Huffman算法 (6)3.2.2 Huffman编码 (6)3.2.3 压缩过程 (6)3.2.4 解压过程 (7)3.3 系统实现方案分析 (7)3.3.1 实现Huffman编码及压缩所需的变量 (7)3.3.2文件名处理 (9)3.3.3 实现Huffman编码及压缩过程所需要的函数 (10)3.3.4 实现解压缩过程所需要的函数 (13)3.3.5 输入输出 (13)4 基于Huffman编码的文件压缩程序的设计 (14)4.1 主模块功能介绍 (14)5 系统的实现 (15)5.1 目标程序运行截图 (15)5.2 测试及测试数据分析 (15)5.2.1 测试数据 (15)5.2.2 测试数据分析 (16)6 总结与展望 (18)参考文献 (19)附录英文缩写词 (20)1任务书题目三哈夫曼编码与文件压缩☐设计目的:掌握二叉树、哈夫曼树的概念,性质与存储结构,能够利用哈夫曼算法实现哈夫曼编码,并应用于文件压缩,从而提高学生综合运用知识的技能与实践能力。

☐设计内容:分析与设计哈夫曼树的存储结构,实现哈夫曼算法以及编码与译码基本功能,并对任意文本文件利用哈夫曼编码进行压缩得到压缩文件,然后进行解压缩得到解压文件。

有兴趣的同学可以查阅资料实现Lempel-Ziv sliding window压缩方法,并与之比较。

用哈夫曼编码实现文件压缩实验报告【管理资料】

用哈夫曼编码实现文件压缩实验报告【管理资料】

《用哈夫曼编码实现文件压缩》实验报告课程名称数据结构B 实验学期 2013 至 2014 学年第一学期学生所在系部计算机学院年级 2013 专业班级学生姓名学号任课教师实验成绩一、实验题目:用哈夫曼编码实现文件压缩二、实验目的:1、了解文件的概念。

2、掌握线性链表的插入、删除等算法。

3、掌握Huffman树的概念及构造方法。

4、掌握二叉树的存储结构及遍历算法。

5、利用Huffman树及Huffman编码,掌握实现文件压缩的一般原理。

三、实验设备与环境:微型计算机、Windows 系列操作系统、Visual C++四、实验内容:根据输入小写英文字母和输入的对应权值创建哈夫曼树,可以求出每个小写英文字母的哈夫曼编码,将文本中的字母对应的哈夫曼编码写入文本中,实现对文本的编码。

五、概要设计:(1)构造Hufffman树的Hufffman算法构造Huffman树步骤:1.根据给定的n个权值{w1,w2,……wn},构造n棵只有根结点的二叉树,起权值为wj。

2.在森林中选取两棵根结点权值最小和次小的树作左右子树,构造一棵新的二叉树,置新二叉树根结点权值为其左右子树根结点权值之和。

3.在森林中删除这两棵树,同时将新得到的二叉树加入森林中。

重复上述两步,直到只含一棵树为止,这棵树即哈夫曼树。

算法结构如图:(2)Huffman编码:数据通信用的二进制编码思想:根据字符出现频率编码,使电文总长最短编码:根据字符出现频率构造Huffman树,然后将树中结点引向其左孩子的分支标“0”,引向其右孩子的分支标“1”;每个字符的编码即为从根到每个叶子的路径上得到的0、1序列。

(3) 文本编码读取存放在文本中的字母,一对一的进行编译,将对应的编码存放到另一个文本中。

#include<>#include<>#include<>//树结点定义typedef struct{int weight;int parent;int lchild;int rchild;}HTNode,*HuffmanTree;static char N[100];//用于保存字符//赫夫曼编码,char型二级指针typedef char **HuffmanCode;//封装最小权结点和次小权结点typedef struct{int s1;int s2;}MinCode;//函数声明void Error();HuffmanCode HuffmanCoding(HuffmanTree &HT,HuffmanCode HC,int *w,int n);MinCode Select(HuffmanTree HT,int n);//当输入1个结点时的错误提示void Error(){printf("一个字符不进行编码!\n");exit(1);}//构造赫夫曼HT,编码存放在HC中,w为权值,n为结点个数HuffmanCode HuffmanCoding(HuffmanTree &HT,HuffmanCode HC,int *w,int n){int i,s1=0,s2=0;HuffmanTree p;char *cd;int f,c,start,m;MinCode min;if(n<=1){Error();//只有一个结点不进行编码,直接exit(1)退出}m=2*n-1;//赫夫曼码需要开辟的结点大小为2n-1HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//开辟赫夫曼树结点空间m+1//初始化n个叶子结点for(p=HT,i=0;i<=n;i++,p++,w++){p->weight=*w;p->parent=0;p->lchild=0;p->rchild=0;}//将n-1个非叶子结点的初始化for(;i<=m;i++,p++){p->weight=0;p->parent=0;p->lchild=0;p->rchild=0;}//构造赫夫曼树for(i=n+1;i<=m;i++){min=Select(HT,i-1);//找出最小和次小的两个结点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;//赋权和}//打印赫夫曼树printf("HT List:\n");printf("Number\t\tweight\t\tparent\t\tlchild\t\trchild\n");for(i=1;i<=m;i++){printf("%d\t\t%d\t\t%d\t\t%d\t\t%d\t\n",i,HT[i].weight,HT[i].parent,HT[i].lchild,HT[i].rchild);}//从叶子结点到根节点求每个字符的赫夫曼编码HC=(HuffmanCode)malloc((n+1)*sizeof(char *));cd=(char *)malloc(n*sizeof(char *));//为赫夫曼编码动态分配空间cd[n-1]='\0';//编码结束符//求叶子结点的赫夫曼编码for(i=1;i<=n;i++){start=n-1;//定义左子树为0,右子树为1/*从最下面的1号节点开始往顶部编码(逆序存放),然后编码2号节点,3号......*/for(c=i,f=HT[i].parent; f!=0; c=f,f=HT[f].parent){if(HT[f].lchild==c)cd[--start]='0';elsecd[--start]='1';}//为第i个字符分配编码空间HC[i]=(char *)malloc((n-start)*sizeof(char *));//将当前求出结点的赫夫曼编码复制到HCstrcpy(HC[i],&cd[start]);}free(cd);return HC;}MinCode Select(HuffmanTree HT,int n){int min,secmin;int temp = 0;int i,s1,s2,tempi = 0;MinCode code ;s1=1;s2=1;min = 999999;//足够大//找出权值weight最小的结点,下标保存在s1中for(i=1;i<=n;i++){if(HT[i].weight<min && HT[i].parent==0){min=HT[i].weight;s1=i;}}secmin = 999999;//足够大//找出权值weight次小的结点,下标保存在s2中for(i=1;i<=n;i++){if((HT[i].weight<secmin) && (i!=s1) && HT[i].parent==0){secmin=HT[i].weight;s2=i;}}//放进封装中=s1;=s2;return code;}void Compression(HuffmanCode HC) //翻译原文档字符为赫夫曼编码FILE *fp1,*fp2;char ch;if((fp1 = fopen("","r")) == NULL)exit(0);if((fp2 = fopen("","a")) == NULL)exit(0);ch = fgetc(fp1);while((int)ch!= -1){switch(ch){case 'a': fputs(HC[1],fp2); break;case 'b': fputs(HC[2],fp2); break;case 'c': fputs(HC[3],fp2); break;case 'd': fputs(HC[4],fp2); break;case 'e': fputs(HC[5],fp2); break;case 'f': fputs(HC[6],fp2); break;case 'g': fputs(HC[7],fp2); break;case 'h': fputs(HC[8],fp2); break;case 'i': fputs(HC[9],fp2); break;case 'j': fputs(HC[10],fp2); break;case 'k': fputs(HC[11],fp2); break;case 'l': fputs(HC[12],fp2); break;case 'm': fputs(HC[13],fp2); break;case 'n': fputs(HC[14],fp2); break;case 'o': fputs(HC[15],fp2); break;case 'p': fputs(HC[16],fp2); break;case 'q': fputs(HC[17],fp2); break;case 'r': fputs(HC[18],fp2); break;case 's': fputs(HC[19],fp2); break;case 't': fputs(HC[20],fp2); break;case 'u': fputs(HC[21],fp2); break;case 'v': fputs(HC[22],fp2); break;case 'w': fputs(HC[23],fp2); break;case 'x': fputs(HC[24],fp2); break;case 'y': fputs(HC[25],fp2); break;case 'z': fputs(HC[26],fp2); break;default: printf(" 没有编码!\n");}ch = fgetc(fp1);}fclose(fp1);fclose(fp2);void main(){HuffmanTree HT=NULL;HuffmanCode HC=NULL;int *w=NULL;int i,n;printf("输入字符:");gets(N);n = strlen(N);w=(int *)malloc((n+1)*sizeof(int *));//开辟n+1个长度的int指针空间w[0]=0;printf("输入结点权值:\n");//输入结点权值for(i=1;i<=n;i++){printf("w[%d]=",i);scanf("%d",&w[i]);}//构造赫夫曼树HT,编码存放在HC中,w为权值,n为结点个数HC=HuffmanCoding(HT,HC,w,n);//输出赫夫曼编码printf("赫夫曼:\n");printf("Number\t\tWeight\t\tCode\n");for(i=1;i<=n;i++){printf("%c\t\t%d\t\t%s\n",N[i-1],w[i],HC[i]);}Compression(HC);}选取权值最小的结点的算法:选取权值次小的结点的算法:哈夫曼树建立的算法:哈夫曼编码的算法:七、测试结果及分析:4.输出哈夫曼树:5.字符对应编码:6.要编码的文本:7.编译后的文本:价。

数据结构哈夫曼编码实验报告

数据结构哈夫曼编码实验报告

数据结构哈夫曼编码实验报告
第一章实验目的
本实验旨在掌握哈夫曼编码的原理和实现方法,并通过编写代码实现一个简单的哈夫曼编码程序。

第二章实验内容
1.理解哈夫曼编码的基本概念和原理。

2.设计并实现一个哈夫曼编码的数据结构。

3.实现哈夫曼编码的压缩和解压缩功能。

4.通过实验验证哈夫曼编码的效果和压缩比。

第三章实验步骤
1.确定实验所需的编程语言和开发环境。

2.定义并实现哈夫曼编码的数据结构。

3.实现哈夫曼编码的压缩和解压缩算法。

4.设计实验样例数据,进行测试和验证。

5.分析实验结果,计算压缩比。

第四章实验结果与分析
1.实验样例数据:________提供一段文本,统计字符出现的频率,并进行哈夫曼编码。

2.实验结果:________展示压缩后的编码结果,计算压缩比。

3.分析:________分析实验结果,讨论哈夫曼编码的效果和优劣。

第五章实验总结与感想
本次实验深入了解了哈夫曼编码的原理和实现方法,通过编写代码实现哈夫曼编码的压缩和解压缩功能。

实验结果表明,哈夫曼编码能够有效地减小数据的存储空间,提高了数据传输的效率。

第六章本文档涉及附件
本实验报告所涉及到的附件包括:________
1.实验代码文件:________.c
2.实验样例数据文件:________.txt
第七章法律名词及注释
1.哈夫曼编码:________一种用于无损数据压缩的编码方法,通过对频率高的字符赋予较短的编码,对频率低的字符赋予较长的编码,从而实现压缩数据的目的。

【数据结构】哈夫曼压缩软件设计_实验报告

【数据结构】哈夫曼压缩软件设计_实验报告

东北大学信息科学与工程学院数据结构课程设计报告题目哈夫曼压缩软件设计课题组长王健课题组成员张颖刘琪张晓雨专业名称计算机科学与技术班级计1307指导教师杨雷2015 年1月课程设计任务书目录1 课题概述 (4)1.1 课题任务 (4)1.2 课题原理 (4)1.3 相关知识 (4)2 需求分析 (5)2.1 课题调研 (5)2.2 用户需求分析 (5)3 方案设计 (5)3.1 总体功能设计 (5)3.2 数据结构设计 (6)3.3 函数原型设计 (6)3.4 主算法设计 (7)3.5 用户界面设计 (9)4 方案实现 (12)4.1 开发环境与工具 (12)4.2 程序设计关键技术 (12)4.3 个人设计实现(按组员分工)4.3.1 王健设计实现 (12)4.3.2 张颖设计实现 (17)4.3.3 刘琪设计实现 (20)4.3.4 张晓雨设计实现 (22)5 测试与调试 (25)5.1 个人测试(按组员分工) (25)5.1.1 王健测试 (25)5.1.2 张颖测试 (26)5.1.3 刘琪测试 (27)5.1.4 张晓雨测试 (31)5.2 组装与系统测试 (32)5.3 系统运行 (32)6 课题总结 (33)6.1 课题评价 (33)6.2 团队协作 (33)6.3 下一步工作 (33)6.4 个人设计小结(按组员分工) (33)6.4.1 王健设计小结 (33)6.4.2 张颖设计小结 (34)6.4.3 刘琪设计小结 (34)6.4.4 张晓雨设计小结 (34)7 附录A 课题任务分工 (35)A-1 课题程序设计分工 (35)A-2 课题报告分工 (36)附录B 课题设计文档(光盘) (37)B-1源程序代码(*.H,*.CPP) (37)B-2工程与可执行文件) (37)附录C 用户操作手册(可选) (37)C.1 运行环境说明 (37)C.2 操作说明 (37)1 课题概述1.1课题任务采用哈夫曼树求得的用于通信的二进制编码称为哈夫曼编码。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

《数据结构》课程设计数学与应用数学一班胡耕岩2012214147一、问题分析和任务定义1.1设计任务采用哈夫曼编码思想实现文件的压缩和恢复功能,并提供压缩前后的占用空间之比。

要求(1)运行时的压缩原文件的规模应不小于5K。

(2)提供恢复文件与原文件的相同性对比功能。

1.2问题分析本课题是利用哈夫曼编码思想,设计对一个文本文件(.txt)中的字符进行哈夫曼编码,生成编码压缩文件,并且还可将一个压缩后的文件进行解码还原为原始文本文件(.txt)。

在了解哈夫曼压缩解压缩原理之前,首先让我们来认识哈夫曼树。

哈夫曼树又称最优二叉树,是带权路径长度最小的二叉树。

在文本文件中多采用二进制编码。

为了使文件尽可能的缩短,可以对文件中每个字符出现的次数进行统计。

设法让出现次数多的字符二进制码短些,而让那些很少出现的字符二进制码长一些。

若对字符集进行不等长编码,则要求字符集中任一字符的编码都不是其它字符编码的前缀。

为了确保哈夫曼编码的唯一性,我们可以对它的左右子树的大小给予比较限定,如:左子树的权值小于右子树的权值。

哈夫曼树中的左右分支各代表‘0’和‘1’,则从根节点到叶子节点所经历的路径分支的‘0’和‘1’组成的字符串,为该节点对应字符的哈夫曼编码。

统计字符中每个字符在文件中出现的平均概率(概率越大,要求编码越短)。

利用哈夫曼树的特点:权越大的叶子离根越近,将每个字符的概率值作为权值,构造哈夫曼树。

则概率越大的节点,路径越短。

哈夫曼译码是从二进制序列的头部开始,顺序匹配成共的部分替换成相应的字符,直至二进制转换为字符序列。

哈夫曼用于文件解压缩的基础是在压缩二进制代码的同时还必须存储相应的编码,这样就可以根据存储的哈夫曼编码对压缩代码进行压缩。

总之,该课题的任务应该是首先要打开要压缩的文本文件并读出其字符出现的频率,以其为权值构建哈夫曼树。

其次要找到构建压缩功能的方法,在构建哈夫曼树的基础上进行编码,改变字符原先的存储结构,以达到压缩文件的目的,以外还有存储相应的哈夫曼编码,为解压缩做准备。

1.3测试用数据本实验的数据是通过读入一个名为huffman.txt的文本文档,文档中内容为字符型数据。

二、概要设计和数据结构的选择以下是在任务分析对题意的理解做出的概要设计和对数据结构的选择:1、数据结构定义//huffman树的结点结构体typedef struct HTnode{long weight; //记录结点的权值int parent; //记录结点的双亲结点位置int lchild; /结点的左孩子int rchild; //结点的右孩子int *code; //记录该结点的huffman编码int codelen; //记录该结点huffman编码的长度//初始化结点,令其权值为无穷大,无双亲及左右孩子HTnode(){weight = MAX;parent = -1;lchild = -1;rchild = -1;codelen = 0;}}HTnode;2、定义huffman数类及其函数class huffmanTree{public:huffmanTree();virtual ~huffmanTree();bool count(char *input); //压缩时统计各字符出现的次数,将其写入对应结点的权值void create(); //压缩时根据各结点的权值构造huffman树void code(); //压缩时利用huffman树计算每个字符的huffman编码void printcode(); //列出每个字符的huffman编码void addbit(int bit); //压缩时对一个未满8个bit的byte中加入一个bitvoid resetbyte(); //将byte清空bool compress(char *input, char *output);//压缩函数,成功返回true 失败falsebool decompress(char *input, char *output); //恢复函数,成功返回true 失败falsevoid compare(char *input, char *output); //将原文件与压缩后的文件比较void compare2(char *input, char *output); //将原文件与恢复后的文件比较private:int root; //记录根结点的位置int leafnum; //记录不同字符的个数HTnode HT[leaf*2-1]; //HTnode结构的数组,用来表示huffman树,树的最大结点个数不会超过leaf*2-1char byte; //压缩文件时用来缓冲bit的变量int bitsnum; //byte中bit的个数int lacknum; //压缩到最后byte中的bit不满8个时填充的0的个数};3、主程序的流程及模块间关系主函数实例化huffmanTree类,并实现菜单工具栏,通过用户的选择输入,用switch语句进行分支执行huffmanTree类中功能函数:1:压缩函数bool compress(char *input, char *output)2:恢复函数bool d ecompress(char *input, char *output)3:恢复文件与原文件的对比函数void compare2(char *input, char *output)并可在完成相应功能后安全退出,压缩或恢复的文件在同文件夹下生成。

三、详细设计和编码核心算法----huffman算法:(1)根据给定的n个权值{w1,w2,……,wn}构成n棵二叉树的集合F={T1,T2,……,Tn},其中每棵二叉树T1中只有一个带权的w1的根据点,其左右子树均空。

(2)在F中选取两棵根结点的权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为其左右树上根结点的权值之和。

(3)在F中删除这两棵树,同时将所得到的二叉树加入F中。

(4)重复(2)(3),直到F中只含一棵树为止。

这棵树便是Huffman树。

Huffman 树可用于构造代码总长度最短的编码方案。

为了详细说明这个问题,特以下面例子来说明:有四个叶子结点A,B,C,D,分别带权为9,4,5,2,可以构成许多种不同的带权二叉树,但各个带权二叉树的WPL(树的带权路径长度)不同,要想由n个带权叶子结点所构成的二叉树中,满二叉树或完全二叉树不一定是最优树。

权值越大的结点离根越近的二叉树才是最优二叉树(huffman树)。

按照上面的算法,则可按照下面图的构造过程生成huffman树。

主程序模块:Huffman编码流程Huffman解码流程四、上机调试以下是我在上机过程中遇到的一些问题及解决方案开始考虑问题是,要对文件进行压缩,如何才能达到比较好的效果,那就huffman编码是采用等长编码还是采用不等长问题,采用不登长编码要避免译码的二义性或多义性。

假设用0表示字符D,用01表示字符C则当接受到编码串“…01…”,并译到字符0时,是立即译出对应的字符D,还是接着与下一个字符1一起译为对应的字符C,这就产生了二义性。

因此,若对某一个字符集进行不等长编码,则要求字符集合中任何一个字符的编码都不能是其他字符编码的前缀。

符合此要求的编码叫做前缀编码。

显然等长编码是前缀编码,这从等长编码所对应的编码二叉树也可以直接看出,任何一个叶子结点都不可能是其它叶子结点的双亲,也就是说,只有当一个结点是另一个结点的双亲时,该结点的字符编码才会是另一个结点的字符编码的前缀。

为了使不等长编码为前缀编码,可用该字符集中的每个字符作为叶子结点生成一棵编码二叉树,为了获得文件的最短长度,特将每个字符的出现频率作为字符结点的权值赋予该结点上,求出此树的最小带权路径长度就等于文件的最短长度。

因此,对文件进行压缩,就可以转化字符集中的所有字符作为叶子结点,字符出现的频率作为权值所产生的huffman 树的问题。

基本思路大致有了后,接下来是对程序的编写工作,程序初步形成后,对其测试,发现了一些语法错误,修正后编译通过。

运行程序如下图所示图5 程序主菜单压缩:在命令行下输入1对文件进行压缩,根据提示输入刚刚建的文本文件(huffman.txt),和要生成的压缩文件名称,按回车确认进行压缩。

图6 压缩文本成功执行完毕后如下图所示。

图7 压缩完毕恢复:在命令行下输入2对本程序压缩的文件进行恢复,根据提示输入待恢复的文件名称和恢复后的文件名称,按回车确定,成功执行后如下图所示。

图7 文件恢复完毕对比:在命令行下输入3对恢复后的文件和原文件对比,根据提示输入要对比的文件,按回车确认,成功执行后如下图所示。

图8 文件恢复完毕五、测试结果程序功能满足设计要求,测试未发现明显bug,详细可参见五使用说明。

程序如下:// stdafx.h#include <iostream> //输入输出头文件#include <fstream> //文件操作的类和方法#include <queue> //队列容器using namespace std;const int leaf = 256; //最多可能出现的不同字符数const long MAX = 99999999; //表示无穷大//huffman树的结点结构体typedef struct HTnode{long weight; //记录结点的权值int parent; //记录结点的双亲结点位置int lchild; //结点的左孩子int rchild; //结点的右孩子int *code; //记录该结点的huffman编码int codelen; //记录该结点huffman编码的长度//初始化结点,令其权值为无穷大,无双亲及左右孩子HTnode(){weight = MAX;parent = -1;lchild = -1;rchild = -1;codelen = 0;}}HTnode;//##############################################################//huffmanTree.h//huffman树类class huffmanTree{public:huffmanTree();virtual ~huffmanTree();bool count(char *input); //压缩时统计各字符出现的次数,将其写入对应结点的权值void create(); //压缩时根据各结点的权值构造huffman树void code(); //压缩时,利用建好的huffman树计算每个字符的huffman编码void printcode(); //列出每个字符的huffman编码void addbit(int bit); //压缩时对一个未满8个bit的byte中加入一个bitvoid resetbyte(); //将byte清空bool compress(char *input, char *output); //压缩函数成功执行返回true 失败falsebool decompress(char *input, char *output); //恢复函数成功执行返回true 失败falsevoid compare(char *input, char *output); //将原文件与压缩后的文件比较void compare2(char *input, char *output); //将原文件与恢复后的文件比较private:int root; //记录根结点的位置int leafnum; //记录不同字符的个数HTnode HT[leaf*2-1]; //HTnode结构的数组,用来表示huffman树,树的最大结点个数不会超过leaf*2-1char byte; //压缩文件时用来缓冲bit的变量int bitsnum; //byte中bit的个数int lacknum; //压缩到最后byte中的bit不满8个时填充的0的个数};//##############################################################//huffmanTree.cpp#include "stdafx.h"#include "huffmanTree.h"//////////////////////////////////////////////////////////////////////// Construction/Destruction//////////////////////////////////////////////////////////////////////huffmanTree::huffmanTree(){//初始化成员变量root = 0;leafnum = 0;byte = 0;bitsnum = 0;lacknum = 0;}huffmanTree::~huffmanTree(){for(int i=0; i<leaf; i++){if(HT[i].codelen != 0)delete []HT[i].code;}}//统计各字符出现的次数bool huffmanTree::count(char *input){ifstream ifs;char c;ifs.open(input,ios::binary);if(!ifs){cout << "无法打开文件" << input << '!' << endl;return false;}while(ifs.get(c)){if(HT[c+128].weight==MAX){ //若该字符是第一次出现,先初始化权值HT[c+128].weight = 0;leafnum++;}HT[c+128].weight++; //权值+1}ifs.close();return true;}//选权值最小的两棵树组成新的数void huffmanTree::create(){for(int i=leaf; i<2*leaf-1; i++){int loc1=-1, loc2=-1;for(int j=0; j<i; j++){if(HT[j].parent != -1)continue;if(loc1==-1 || HT[j].weight < HT[loc1].weight){loc2 = loc1;loc1 = j;}else if(loc2==-1 || HT[j].weight < HT[loc2].weight)loc2 = j;}if(HT[loc1].weight==MAX || HT[loc2].weight==MAX || loc2==-1) //只剩一棵树,结束break;HT[i].weight = HT[loc1].weight + HT[loc2].weight;//为了减少压缩文件中需要写入的huffman树的信息,约定小标小的结点做为双亲结点的左孩子HT[i].lchild = loc1>loc2 ? loc2 : loc1;HT[i].rchild = loc1>loc2 ? loc1 : loc2;HT[loc1].parent = i; HT[loc2].parent = i;root = i;}}//列出每个字符的huffman编码void huffmanTree::printcode(){for(int i=0; i<leaf; i++){if(HT[i].codelen!=0){cout << "值为" << i-128 << "的字符的huffman编码:";for(int j=0; j<HT[i].codelen; j++){cout << HT[i].code[j];}cout << endl;}}}//压缩时,利用建好的huffman树计算每个字符的huffman编码void huffmanTree::code(){for(int i=0; i<leaf; i++){int len=0;int loc=i;while(HT[loc].parent!=-1){ //计算huffman编码长度len++;loc = HT[loc].parent;}HT[i].codelen = len;HT[i].code = new int[len];loc = i;for(int j=len-1; j>=0; j--){ //从后往前找,记录结点的huffman编码if(loc==HT[HT[loc].parent].lchild)HT[i].code[j] = 0;elseHT[i].code[j] = 1;loc = HT[loc].parent;}}}//压缩时对一个未满8个bit的byte中加入一个bitvoid huffmanTree::addbit(int bit){if(bit == 0)byte = byte << 1; //若新增的bit为0,则直接将byte按位左移elsebyte = ((byte << 1) | 1); //若新增的bit为1,先将byte按位左移,再与1按位或运算bitsnum++;}//将byte清空void huffmanTree::resetbyte(){byte = 0;bitsnum = 0;}//压缩函数成功执行返回true 失败falsebool huffmanTree::compress(char *input, char *output){if( !count(input) )return false;create();code();ifstream ifs;ofstream ofs;ifs.open(input,ios::binary);ofs.open(output,ios::binary);char c;if(!ifs){cout << "无法打开文件" << input << '!' << endl;return false;}if(!ofs){cout << "无法打开文件" << output << '!' << endl;return false;}ofs.put(0); //预留一个字符,等压缩完后在该位置写入不足一个byte的bit个数ofs.put(root-384); //将根节点的位置-384写入(为使该值不超过char的最大表示范围)for(int i=0; i<leaf*2-1; i++){ //写入每个结点的双亲结点位置if(HT[i].parent==-1) //若该节点没有双亲结点,则写入127(一个字节所能表示的最大值)ofs.put(127);else //否则将双亲结点的位置-384再写入(为使该值不超过char的最大表示范围)ofs.put(HT[i].parent-384);}while(ifs.get(c)){ //将字符的huffman编码并加入byte中int tmp = c+128;for(int i=0; i<HT[tmp].codelen; i++){addbit(HT[tmp].code[i]);if(bitsnum==8){ //若byte已满8位,则输出该byte并将byte清空ofs.put(byte);resetbyte();}}}if(bitsnum!=0){ //处理最后未满8个字符的byte,用0填充并记录填充的个数for(int i=bitsnum; i<8; i++){addbit(0);lacknum++;}ofs.put(byte);resetbyte();}ofs.seekp(0,ios::beg); //将写指针移动到文件开头ofs.put(lacknum); //写入最后一个字节缺失的bit个数ifs.close();ofs.close();return true;}//恢复函数成功执行返回true 失败falsebool huffmanTree::decompress(char *input, char *output){queue<char> q;char c;ifstream ifs;ofstream ofs;ifs.open(input,ios::binary);ofs.open(output,ios::binary);if(!ifs){cout << "无法打开文件" << input << '!' << endl;return true;}if(!ofs){cout << "无法打开文件" << output << '!' << endl;return false;}ifs.get(c);lacknum = c; //读出最后一个字节缺失的bit个数ifs.get(c);root = c+384; //读出根结点的位置for(int i=0; i<leaf*2-1; i++){ //建立各结点之间的双亲孩子关系ifs.get(c);if(c==127)continue;else{HT[i].parent = c+384;if(HT[c+384].lchild==-1)HT[c+384].lchild = i;elseHT[c+384].rchild = i;}}int point = root;//为了方便处理最后一个可能有缺失bit的字节,先将读出的数据放入队列while(ifs.get(c))q.push(c);//还原文件过程while(q.size()>1){ //还未到最后一个字节c = q.front();for(int i=0; i<8; i++){if(int(c&128)==0){point = HT[point].lchild;if(HT[point].lchild==-1 && HT[point].rchild==-1){ofs.put(char(point-128));point = root;}c = c << 1;}else{point = HT[point].rchild;if(HT[point].lchild==-1 && HT[point].rchild==-1){ofs.put(char(point-128));point = root;}c = c << 1;}}q.pop();}c = q.front(); //最后一个字节for(i=0; i<8-lacknum; i++){if(int(c&128)==0){point = HT[point].lchild;if(HT[point].lchild==-1 && HT[point].rchild==-1){ofs.put(char(point-128));point = root;}c = c << 1;}else{point = HT[point].rchild;if(HT[point].lchild==-1 && HT[point].rchild==-1){ofs.put(char(point-128));point = root;}c = c << 1;}}q.pop();ifs.close();ofs.close();return true;}//将原文件与压缩后的文件比较void huffmanTree::compare(char *input, char *output) {ifstream origin, compress;origin.open(input,ios::binary);compress.open(output,ios::binary);if(!origin){cout << "无法打开文件" << input << '!' << endl;return;}if(!compress){cout << "无法打开文件" << output << '!' << endl;return;}double total1=0, total2=0;char c;while(origin.get(c))total1++;while(compress.get(c))total2++;cout << "原文件大小:" << total1 << " Byte" << endl;cout << "压缩后大小:" << total2 << " Byte" << endl;cout << "压缩率:" << total2/total1*100 << '%' << endl;origin.close();compress.close();}//将原文件与恢复后的文件比较void huffmanTree::compare2(char *input, char *output){ifstream origin, decompress;origin.open(input,ios::binary);decompress.open(output,ios::binary);double total1=0, total2=0;char c1, c2;bool dif = false;while(origin.get(c1) && decompress.get(c2)){if(c1!=c2) //依次比较每个字节,不同则将dif标志设为true dif = true;total1++;total2++;}while(origin.get(c1)){ //若原文件还有剩余的数据,将dif设为truedif = true;total1++;}while(decompress.get(c2)){ //若恢复文件还有剩余的数据,将dif设为truedif = true;total2++;}cout << "原文件大小:" << total1 << " Byte" << endl;cout << "恢复文件大小:" << total2 << " Byte" << endl;if(dif==true)cout << "原文件与恢复文件不同!" << endl;elsecout << "原文件与恢复文件相同!" << endl;origin.close();decompress.close();}//############################################################## //huffman.cpp#include "stdafx.h"#include "huffmanTree.h"void main(){int choice = 1;char input[255], output[255];huffmanTree h;while(choice){cout<<" ***************************************************"<<endl;cout<<" * 哈夫曼编码压缩恢复算法*"<<endl;cout<<" * *"<<endl;cout<<" * 1)压缩*"<<endl;cout<<" * *"<<endl;cout<<" * 2) 恢复*"<<endl;cout<<" * *"<<endl;cout<<" * 3) 恢复文件与原文件的对比*"<<endl;cout<<" * *"<<endl;cout<<" * 4) 清屏*"<<endl;cout<<" * *"<<endl;cout<<" * 5) 退出*"<<endl;cout<<" * *"<<endl;cout<<" * 说明:请您输入相应的操作序号进行操作*"<<endl;cout<<" ****************************************************"<<endl;cout<<">";cin >> choice;switch(choice){case 1:{cout << "请输入待压缩的文件名:";cin >> input;cout << "请输入压缩后的文件名:";cin >> output;if( press(input,output)){h.printcode();pare(input,output);cout<<endl<<"文件压缩成功!"<<endl;}else{cout<<endl<<"文件压缩失败!"<<endl;}}break;case 2:{cout << "请输入待恢复的文件名:";cin >> input;cout << "请输入恢复后的文件名:";cin >> output;if (h.decompress(input,output))cout<<endl<<"文件恢复成功!"<<endl;elsecout<<endl<<"文件恢复失败!"<<endl;}break;case 3:{cout << "请输入原文件的文件名:";cin >> input;cout << "请输入恢复文件的文件名:";cin >> output;pare2(input,output);}break;case 4:{//执行清屏命令system("cls");}break;case 5:break;default:cout << "参数错误!请重新输入" << endl;}cout << endl;}}。

相关文档
最新文档