哈夫曼编码算

合集下载

哈夫曼编码

哈夫曼编码

第三次上机报告哈夫曼编码一.上机目的通过构造huffman树(最优二叉树)来加深对于二叉树这一数据结构特点的理解,体会二叉树在实际生活中的应用价值。

利用堆实现排序,体会堆这一数据结构在排序与有序提取数据中的价值。

熟悉文件输入输出。

加深对于ASCII码的理解以及熟练char型数据的使用。

熟练使用按位操作。

二.问题描述已知26个英文小写字母的字频,将其用二进制编码,使得采用这种编码进行通信时,信息传输量最小。

并且,为了在字符间省去不必要的间隔符号,要求所给出的每一个字符的编码在系统中都能唯一确定。

即任意字符的二进制编码不能解释为其他字符编码的前缀。

以上问题可以转化为如何构造一棵具有最小带权外部路径长度的扩充二叉树(huffman树)的问题。

具体的构造方法见算法部分的说明。

要求给定一个字符序列,将其转化为对应的huffman编码,即编码过程。

要实现这一目标,可以在huffman树构造好后,通过深度周游,将26个字母对应的huffman编码记录下来并且构造一个编码本codebook,利用ASCII码中a到z的连续性,将codebook 中字母顺序也排成a到z。

编码本构造好后,通过查阅编码本就可以很容易的得到字符序列的huffman编码。

另外,还要求给定一个huffman编码序列,将其转化为26个小写英文字母的序列,即解码过程。

为此,只需要按照huffman编码序列的顺序,周游已构造好的huffman树,找到对应的外部结点,并读取外部结点的字符即可。

这里,在构造huffman树时,我使用了堆来对huffmantreenode进行排序。

在编码时,采用按位操作。

三.数据结构与算法数据结构:小根堆类模板:template <class T>class minheap{private:T * heaparray;intcurrentsize;intmaxsize;void buildheap(T * hmtn);//建堆void swap(intpos_x,intpos_y);public:minheap(){};minheap(T * hmtn,int n);//构造函数,n为堆的最大元素数目virtual ~ minheap(){delete [] heaparray;};//析构函数bool remove(intpos,T& node);//删除给定下标的元素void siftup(int position);//从position开始向上调整void siftdown(int left);//从left开始向下调整friend class huffmantreenode;boolisempty();//判断堆是否为空boolisleaf(intpos) const;//判断是否为叶子结点intleftchild(intpos) const;//返回左孩子位置intrightchild(intpos) const;//返回右孩子位置int parent(intpos) const;//返回父结点位置bool insert(T &newnode);//插入新元素newnodeT &removemin();//从堆顶删除最小值};Huffmantree类:class huffmantree{private:huffmantreenode * root;public:huffmantree(huffmantreenode * hmtn,int n);//由一组huffmantreenode生成一个huffmantreehuffmantreenode * getroot() {return root;}virtual ~huffmantree(){}void deletetree(huffmantreenode * root);void encode(huffmantreenode * root,codebook * cb,unsignedinthuffcode,intcodenum);//产生编码,返回到codebook中void decode(char code[],char result[]);//解码,将结果放入result中friend class huffmantreenode;friend class minheap;};Huffmantreenode类:class huffmantreenode{private:huffmantreenode * left;//左子树huffmantreenode * right;//右子树float weight; //权重char oricode; //源码public:huffmantreenode(){left=NULL;right=NULL;}huffmantreenode(huffmantreenode * l,huffmantreenode * r){//给定数据的构造函数oricode=0;weight=l->getweight()+r->getweight();left=l;right=r;}float getweight() {return weight;};//返回数据char getoricode() {return oricode;}//返回字母huffmantreenode * leftchild() {return left;}//返回左子树huffmantreenode * rightchild() {return right;}//返回右子树void copy(huffmantreenode&hmtn) {left=hmtn.leftchild();right=hmtn.rightchild();weight=hmtn.getweight();oricode=hmtn.getoricode();}void merge(huffmantreenode&l,huffmantreenode& r){//将两棵树合并为一棵新树,其权重为两棵子树权重之和oricode=0;weight=l.getweight()+r.getweight();left=&l;right=&r;}void setinfo(char oric,floatwei) {oricode=oric; weight=wei;}boolisleaf() {//判断是否为叶子结点return(left==NULL&&right==NULL);}};编码本codebook类:class codebook{private:char oricode;//源码unsigned inthuffmcode;//huffman编码,按位操作intcodenum;//huffmancode的位数public:codebook(){oricode=0;huffmcode=0;codenum=0;}codebook(char &oric,unsignedint&hfmc){oricode=oric;huffmcode=hfmc;}~codebook(){}char getoricode() {return oricode;}void getinfo(char &ori,int&huff,int&num){//获取信息ori=oricode;huff=huffmcode;num=codenum;}void print() {cout<<"源码:"<<oricode;cout<<" 哈夫曼编码:";unsigned int m=0x00000001<<(codenum-1);while(m!=0x00000000){cout<<(huffmcode&m? '1':'0');m>>=1;}cout<<endl;}void setinfom(char oric,unsignedinthmc,int cum){//设定信息oricode=oric;huffmcode=hmc;codenum=cum;}friend void printcode(char message[],codebook * cb);};算法:非类成员函数:void printcode(char message[],codebook * cb){//打印哈夫曼编码int i(0);unsigned int m;ofstreamfout("huffmancode.txt");//打开文件输出到"huffmancode.txt"while(message[i]!='\0'){//字符串未到结尾时if (message[i]<'a'||message[i]>'z') fout<<message[i];//如果输入的字符不在a到z 之间则不编码,照源码输出else {m=0x00000001<<(cb[message[i]-'a'].codenum-1);//将1移到编码的最高位while(m!=0x00000000){//当1还在m中时fout<<((cb[message[i]-'a'].huffmcode)&m? '1':'0');//按位与,找到相应位置的huffmancode是1还是0;m>>=1;//1右移}}i++;}cout<<"编码完毕,结果已输出到文件huffmancode.txt中"<<endl;fout.close();//关闭文件输出}void printhuffmancode(codebook cb[M]){//打印哈夫曼编码表ofstreamfout("huffmancodelist.txt");char oric;inthuffmcode,codenum,i;for(i=0;i<M;i++){cb[i].getinfo(oric,huffmcode,codenum);fout<<"源码:"<<oric;fout<<" 哈夫曼编码:";unsigned int m=0x00000001<<(codenum-1);while(m!=0x00000000){fout<<(huffmcode&m? '1':'0');m>>=1;}fout<<endl;}fout.close();cout<<"哈夫曼编码表已输出到文件huffmancodelist.txt中"<<endl;}以下着重分析一下构造哈夫曼树的算法、编码算法、解码算法以及按位输出的算法。

哈夫曼编码设计原理

哈夫曼编码设计原理

哈夫曼编码设计原理哈夫曼编码(HuffmanCoding)是一种十分重要的信息压缩编码技术,它能够给予信息流最有效的编码,一般情况下它能够实现信息量的压缩,从而节省载体存储和信息传输的带宽和时间。

因此,哈夫曼编码是许多数据传输、压缩和存储应用中不可缺少的一部分。

哈夫曼编码基于信息熵的概念,是由美国著名科学家卡尔哈夫曼在1952年提出来的,他提出了一种构建最优编码树的算法,这种编码树也被称为哈夫曼树。

哈夫曼编码原理基于概率编码理论,该理论规定是:为了实现最优编码,必须给出根据待编码字符及其出现概率给出的编码的编码顺序,以此构建哈夫曼树,来实现最优编码。

哈夫曼树的构建方法如下: 1)计算每个字符的概率,并按从大到小的顺序排列。

2)选择概率最小的两个字符,合并成一个新的结点,结点概率为两者概率之和,新形成的结点也排在概率队列的最后。

3)重复上述过程,直到所有的字符都被归入一个结点中,得到一棵哈夫曼树。

值得注意的是,哈夫曼编码的最优性能实际上是由哈夫曼树的构建方法来实现的,而哈夫曼树的构建是基于概率编码理论的基础上进行的,因此,只有给定有限规模的字符集,而且这些字符出现的概率必须符合概率编码理论,哈夫曼编码才能正常工作,从而达到压缩效果。

此外,哈夫曼编码还有许多其他优势,比如它可以实现稳定性和循环利用性,并且可以根据不同的信息实时分析和更新,从而达到准确无误的编码效果。

此外,由于它的编码方案是可以被逆向变换的,因此,解码的效果也能够超过预想的期望,从而使得解码效率得到显著提高。

总之,哈夫曼编码的实际应用使得它在信息压缩、数据传输以及数据存储等领域发挥着至关重要的作用。

它能够有效减少信息量,帮助节省带宽,使传输成本大大降低,从而提高数据传输和存储的效率,并能够释放更多的内存空间,从而提高信息传输的速度,大大改善数据传输的效率。

哈夫曼编码简单例题

哈夫曼编码简单例题

哈夫曼编码简单例题哈夫曼编码是一种用于编码数据的算法,它使用变长编码来把数据进行压缩编码。

哈夫曼编码基于一系列数学模型,以及相关编码字符的出现频率,为数据提供一种有效的编码模式,能够做到节省存储空间和传输时间。

关于哈夫曼编码,最简单的例题可能就是一张字典,比如下面这样:字符t出现频率At0.45Bt0.13Ct0.12Dt0.09Et0.08Ft0.07Gt0.07对于上面的字典,使用哈夫曼编码的步骤如下:第一步:将出现频率转换为权值,即将每个字符的出现频率转化为整数,并构建权值树。

At45Bt13Ct12Dt9Et8Ft7Gt7第二步:合并权值最小的两个字符,以及更新权值树:At45Bt13Ct12Dt9Et8F,Gt7第三步:给每个字符赋予编码,并将它们添加到哈夫曼编码表中。

在这里,A是编码中最长的字符,所以它将拥有最多的编码位0,而F和G是编码中最短的字符,它们共享最少的编码位1,最终得出的编码表如下:At0Bt100Ct101Dt1100Et1101F,Gt11第四步:编码字符串:接下来我们来编码一个字符串,比如“ABCDE”,根据上面的哈夫曼编码表,我们可以将每个字符的编码连接起来,最终得到一个01串“00010111001101”,这就是ABCDE 的哈夫曼编码。

可以看出,使用哈夫曼编码能够大大节省编码所需要的空间,使得传输和存储都变得更加有效率。

此外,由于哈夫曼编码可以获得更高的编码效率,因此它在数据编码领域受到了广泛的应用。

它已成为许多数据压缩、传输、存储标准的基础,比如说JPEG图像、MPEG视频和ICMP协议等。

由于哈夫曼编码是一种节省空间并增加传输效率的编码方式,它在许多行业中得到了广泛的应用,比如说在媒体、电信、计算机和安全等行业都有广泛的应用。

它能够压缩数据以节省传输时间和空间,同时保持数据的完整性。

此外,哈夫曼编码也被应用在视频编解码、数据压缩、网络通信协议等场景,发挥着重要作用。

数据结构 课程设计之哈夫曼编码

数据结构  课程设计之哈夫曼编码

(一) 哈夫曼树的设计思想对于一组具有确定权值的叶子结点可以构造出多个具有不同带权路径长度的二叉树,其中具有最小带权路径长度的二叉树称作哈夫曼树或者最优二叉树。

首先给定n 个权值创造n 个只含根结点的二叉树,得到一个二叉树林;再在这二叉树林里面找根结点的权值最小和次小的两棵树作成新的二叉树,其中新的二叉树的根结点的权值为摆布子根结点权值之和;最后在二叉树林中把组合过的二叉树删除,再重复第二步,直到最后就剩一颗二叉树的时候得到的这棵二叉树就是哈夫曼树。

(二)哈夫曼编码与解码的设计思想在数据通讯中,时常要将传送的文字转换为二进制字符0 和1 组成的二进制串,称这个过程为编码。

与子相对的是解码或者是译码,就是用与编码相同的方式将二进制串转换称编码前的文字的过程称作解码。

在这里是通过哈夫曼树实现编码与解码的,所以称作是哈夫曼编码与解码。

首先输入一个字符串,还有相应的在哈夫曼树里的权值,这样用哈夫曼树把字符串用二进制串代替它,这个过程要注意树和编码问题,其中树的问题在上面已经解决,主要看编码的问题,就是根据我们输入的字符串和权值建立相应的树模型,这一步完成那编码就已经完成为了,最后打印就行了;然后就是解码,完成编码相应的解码就相对简单了,就是先找到在编码的时候建的那个模型树,将编码中的二进制串再根据权值转换为相应的字符串,这样一步步解码就行了。

以上就是通过用哈夫曼树进行哈夫曼编码与解码如何实现的主要设计思想。

(一)哈夫曼树的流程图不 是图 1 哈夫曼树的流程图(二)编码与解码的流程图图 2 编码与解码的流程图图片说明: (左边)编码流程图, (右边)解码流程图。

开始输入字符串判断权值 建立路径有最小和次小 循环建立二叉树根据树对路径分左 0右 1写出对应结点的编码结束开始初始化哈夫曼链表二叉树林找最小和次小 的二叉树组合成新的二叉树 删除用过的二叉树是不是最后一 个二叉树是结束开始找到树的根结点 输入二进制串扫描根据树的路径打印对应字符继续扫描 是否结束是输出字符串结束否下面给出的是用中缀转后缀算法实现的程序的源代码:#include "stdio.h"#include "string.h"#define MAX 100struct HaffNode{int weight;int parent;char ch;int lchild;int rchild;}*myHaffTree;struct Coding{char bit[MAX];char ch;int weight;}*myHaffCode;void Haffman(int n){int i,j,x1,x2,s1,s2;for (i=n+1;i<=2*n-1;i++) {s1=s2=10000;x1=x2=0;for (j=1;j<=i-1;j++)/*定义常量*//*权值*//*双亲结点下标*//*构造哈夫曼树*//*定义数组*//*字符的权值*//*定义结构体*//*定义哈夫曼函数*//*树的初始化*//*构造哈夫曼树的非叶子结点*/{if(myHaffTree[j].parent==0&&myHaffTree[j].weight<s1){s2=s1;x2=x1;s1=myHaffTree[j].weight;x1=j;/*分配摆布结点*/}else if(myHaffTree[j].parent==0&&myHaffTree[j].weight<s2){s2=myHaffTree[j].weight;x2=j;}}myHaffTree[x1].parent=i;myHaffTree[x2].parent=i;myHaffTree[i].weight=s1+s2;myHaffTree[i].lchild=x1;myHaffTree[i].rchild=x2;/*摆布子组合为新树*/}}void HaffmanCode(int n){int start,c,f,i,j,k;char *cd;/*构造n 个结点哈夫曼编码*/cd=(char *)malloc(n*sizeof(char));myHaffCode=(struct Coding *)malloc((n+1)*sizeof(struct Coding));cd[n-1]='\0';for(i=1;i<=n;++i) /*n 个叶子结点的哈夫曼编码*/ {start=n-1;for(c=i,f=myHaffTree[i].parent;f!=0;c=f,f=myHaffTree[f].parent)if(myHaffTree[f].lchild==c) cd[--start]='0';else cd[--start]='1';for(j=start,k=0;j<n;j++){myHaffCode[i].bit[k]=cd[j];k++;}myHaffCode[i].ch=myHaffTree[i].ch; myHaffCode[i].weight=myHaffTree[i].weight; }free(cd);}Init(){int i,n,m;printf("please input the number of words:"); scanf("%d",&n); /*取编码对应的权值*//*定义有返回值的函数*/m=2*n-1;myHaffTree=(struct HaffNode *)malloc(sizeof(struct HaffNode)*(m+1)); for(i=1;i<=n;i++){printf("please input the word and the equal:");scanf("%s%d",&myHaffTree[i].ch,&myHaffTree[i].weight); myHaffTree[i].parent=0;myHaffTree[i].lchild=0;myHaffTree[i].rchild=0;}for(i=n+1;i<=m;i++){myHaffTree[i].ch ='#';myHaffTree[i].lchild=0;myHaffTree[i].parent=0;myHaffTree[i].rchild=0;myHaffTree[i].weight=0;}Haffman(n);HaffmanCode(n);for(i=1;i<=n;i++){printf("%c %d",myHaffCode[i].ch,myHaffCode[i].weight); printf("\n");}printf("init success!\n");return n;}void Caozuo_C(int m){int n,i,j;char string[50],*p;printf("please input the words :"); scanf("%s",string);n=strlen(string);for(i=1,p=string;i<=n;i++,p++){for(j=1;j<=m;j++)if(myHaffCode[j].ch==*p)printf("%s\n",myHaffCode[j].bit); }}void Caozuo_D(int n){int i,c;char code[1000],*p;printf("please input the coding:"); scanf("%s",code);for(p=code,c=2*n-1;*p!='\0';p++) {if(*p=='0'){c=myHaffTree[c].lchild;if(myHaffTree[c].lchild==0){printf("%c",myHaffTree[c].ch);c=2*n-1;continue;/* 编码函数*//*计算字符串长度*/ /*进行编码*//*解码函数*//*输入二进制编码*//*进行解码*//*结束条件*//*赋值*//* 扫描*//*结束*/}}else if(*p=='1'){c=myHaffTree[c].rchild;if(myHaffTree[c].lchild==0){printf("%c",myHaffTree[c].ch);c=2*n-1; /*赋值*/continue;}}}printf("\n");}void main(){int n;char char1;n=Init();printf("A.coding B.codeprintingwhile(1){scanf("%c",&char1);if(char1=='c')break;switch(char1){case'A':Caozuo_C(n);break;case'B':Caozuo_D(n);break;case'C':;break;}}}/*主函数*//*定义字符*//*函数的调用*/C.exit\nplease input the process:\n");/*判断字符*//*执行编码操作*//*执行解码操作*/哈夫曼编码与解码的实现(一)中缀转后缀算法的运行结果:这部份我主要遇到了如下三个问题,其内容与解决方法如下所列:问题1:刚开始不知道如何建一个好树,因为我开始试着建了几个二叉树,不知道什么原因运行的时候那编码总是不对,跟在草稿纸上自己画的那个二叉树总是不相符,就找原因。

哈夫曼编码计算

哈夫曼编码计算

哈夫曼编码是一种根据字符出现频率创建的编码方式,其中频率高的字符使用较短的编码,频率低的字符使用较长的编码。

以下是计算哈夫曼编码的步骤:
1. 创建一个森林,每个字符出现频率作为一棵树的权值,每个树只有一个节点。

2. 从森林中取出两棵权值最小的树,合并它们,生成一棵新的树。

新树的权值是这两棵树的权值之和,左子树是原来的左树,右子树是原来的右树。

3. 将新生成的树放回森林中。

4. 重复步骤2和3,直到森林中只剩下一棵树为止,这棵树就是哈夫曼树。

5. 哈夫曼编码是从哈夫曼树的根节点到叶节点的路径,按照从左到右的顺序,用0和1表示路径的方向。

举个例子,假设我们有4个字符(a、b、c、d),它们的出现频率分别为1、2、3、4。

根据这些频率,我们可以建立以下森林:
1. a -> 1
2. b -> 2
3. c -> 3
4. d -> 4
然后,我们按照上述步骤合并权值最小的两个节点,生成新的节点,并反复进行这个过程,直到得到一棵只有根节点的树。

最后,从根节点到每个叶节点的路径就是每个字符的哈夫曼编码。

需要注意的是,哈夫曼编码是一种无损压缩算法,它不会丢失原始数据的信息。

但是,它并不适用于所有情况,特别是当字符出现频率相差很大时,哈夫曼编码的效果可能会受到影响。

哈夫曼编码与解码

哈夫曼编码与解码

哈夫曼编码与解码
哈夫曼编码(Huffman coding)和哈夫曼解码(Huffman decoding)是一种用于数据压缩的技术,由美国计算机科学家 David A. Huffman 于 1952 年提出。

哈夫曼编码的基本思想是根据字符在文本中出现的频率来分配二进制编码的长度。

出现频率较高的字符将被分配较短的编码,而出现频率较低的字符将被分配较长的编码。

这样,通过使用较短的编码来表示常见字符,可以实现更有效的数据压缩。

哈夫曼编码的过程包括以下步骤:
1. 统计字符出现频率:对要编码的文本进行分析,统计每个字符出现的次数。

2. 构建哈夫曼树:根据字符出现频率构建一棵二叉树,其中频率较高的字符靠近树的根节点,频率较低的字符位于树的叶子节点。

3. 分配编码:从根节点开始,根据字符出现频率为每个字符分配二进制编码。

左子节点表示 0,右子节点表示 1。

4. 编码文本:将文本中的每个字符替换为其对应的哈夫曼编码。

哈夫曼解码是哈夫曼编码的逆过程,用于将已编码的数据还原为原始文本。

解码过程根据哈夫曼树的结构和编码规则,从编码中解析出原始字符。

哈夫曼编码与解码在数据压缩领域具有广泛的应用,例如图像、音频和视频压缩。

它通过有效地利用字符频率分布的不均匀性,实现了较高的压缩率,从而减少了数据传输和存储的开销。

需要注意的是,哈夫曼编码是一种无损压缩技术,意味着解码后可以完全还原原始数据。

但在实际应用中,可能会结合其他有损压缩技术来进一步提高压缩效果。

哈夫曼编码详解

哈夫曼编码详解

哈夫曼编码详解前两天发布那个 rsync 算法后,想看看数据压缩的算法,知道⼀个经典的压缩算法 Huffman 算法。

你应该听说过和他的经典的压缩算法—— ,这是⼀种通过字符出现频率,,和⼆叉树来进⾏的⼀种压缩算法,这种⼆叉树⼜叫 Huffman ⼆叉树 —— ⼀种带权重的树。

但是⽹上查了⼀下,中⽂社区内好像没有把这个算法说得很清楚的⽂章,尤其是树的构造,⽽正好看到⼀篇国外的⽂章《》,其中的例⼦浅显易懂,相当不错,我就转了过来。

注意,我没有对此⽂完全翻译。

我们直接来看⽰例,如果我们需要来压缩下⾯的字符串: “beep boop beer!” ⾸先,我们先计算出每个字符出现的次数,我们得到下⾯这样⼀张表 :字符次数‘b’3‘e’4‘p’2‘ ‘2‘o’2‘r’1‘!’1 然后,我把把这些东西放到 Priority Queue 中(⽤出现的次数据当 priority),我们可以看到,Priority Queue 是以 Prioirry 排序⼀个数组,如果 Priority ⼀样,会使⽤出现的次序排序:下⾯是我们得到的 Priority Queue: 接下来就是我们的算法——把这个 Priority Queue 转成⼆叉树。

我们始终从 queue 的头取两个元素来构造⼀个⼆叉树(第⼀个元素是左结点,第⼆个是右结点),并把这两个元素的 priority 相加,并放回 Priority 中(再次注意,这⾥的 Priority 就是字符出现的次数),然后,我们得到下⾯的数据图表: 同样,我们再把前两个取出来,形成⼀个 Priority 为2+2=4的结点,然后再放回 Priority Queue 中 : 继续我们的算法(我们可以看到,这是⼀种⾃底向上的建树的过程): 最终我们会得到下⾯这样⼀棵⼆叉树: 此时,我们把这个树的左⽀编码为0,右⽀编码为1,这样我们就可以遍历这棵树得到字符的编码,⽐如:‘b’的编码是 00,’p'的编码是101, ‘r’的编码是 1000。

哈夫曼编码算法详解

哈夫曼编码算法详解

哈夫曼编码算法详解在计算机科学中,哈夫曼编码是一种压缩算法,也叫做霍夫曼编码,是由霍夫曼(Huffman)在1952年首创的。

霍夫曼编码是一种无损压缩算法,可以对文本文件、音频文件、图像文件等各种类型的文件进行压缩。

1. 哈夫曼编码的原理哈夫曼编码是基于频率统计的思想,通过统计每个字符在文件中出现的频率,选择出现频率最高的字符,将其映射为一组比特位,出现频率较低的字符则映射为比高的比特位,从而实现对文件的压缩。

通过哈夫曼编码,可以将文件压缩到原始大小的一半甚至更小。

2. 哈夫曼编码的实现哈夫曼编码的实现需要进行几个步骤:2.1 统计字符的出现频率从文件中读取字符,统计每个字符在文件中出现的次数,可以使用一个数组或字典来保存每个字符的出现次数。

对于英文文本来说,出现频率最高的字符是空格,其次是字母“e”。

2.2 构建哈夫曼树将所有的字符按照出现频率从小到大排序,选出出现频率最小的两个字符作为左右子节点,其父节点的出现频率为左右子节点出现频率之和。

重复这个过程,直到节点数为1,这样就得到了一棵哈夫曼树。

2.3 生成哈夫曼编码从哈夫曼树的根节点开始,遍历所有的节点,将左子节点标记为0,将右子节点标记为1,将所有的叶子节点的字符和对应的哈夫曼编码保存到一个字典中。

最终得到了每个字符对应的哈夫曼编码。

2.4 进行压缩将文件中每个字符替换为对应的哈夫曼编码,然后将所有的哈夫曼编码拼接成一个二进制数,在最后不足8位的位置补零,将其存储到文件中。

这样就完成了文件的压缩。

3. 哈夫曼编码的优点哈夫曼编码具有以下优点:3.1 压缩率高由于哈夫曼编码是根据不同字符的出现频率来进行编码的,出现频率高的字符用较短的编码表示,出现频率低的字符用较长的编码表示,能够最大限度地减少文件的大小,从而达到高的压缩率。

3.2 唯一解哈夫曼编码是通过构建哈夫曼树来得到每个字符对应的编码,哈夫曼树的构建是唯一的,因此哈夫曼编码也是唯一的。

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

实验三树的应用一.实验题目:树的应用——哈夫曼编码二.实验内容:利用哈夫曼编码进行通信可以大大提高信道的利用率,缩短信息传输的时间,降低传输成本。

根据哈夫曼编码的原理,编写一个程序,在用户输入结点权值的基础上求哈夫曼编码。

要求:从键盘输入若干字符及每个字符出现的频率,将字符出现的频率作为结点的权值,建立哈夫曼树,然后对各个字符进行哈夫曼编码,最后打印输出字符及对应的哈夫曼编码。

三、程序源代码:#include <iostream>#include <fstream>#include <string.h>#include <stdlib.h>typedef struct{char data;int weight;int parent,lchild,rchild;}HTNode,*HuffmanTree;typedef char * * HuffmanCode;void Select(HuffmanTree &HT,int n,int m){HuffmanTree p=HT;int tmp;for(int j=n+1;j<=m;j++){int tag1,tag2,s1,s2;tag1=tag2=32767;for(int x=1;x<=j-1;x++){ if(p[x].parent==0&&p[x].weight<tag1){ tag1=p[x].weight;s1=x;}}for(int y=1;y<=j-1;y++){ if(p[y].parent==0&&y!=s1&&p[y].weight<tag2){ tag2=p[y].weight;s2=y;}}if(s1>s2) //将选出的两个节点中的序号较小的始终赋给s1{ tmp=s1; s1=s2; s2=tmp;}p[s1].parent=j;p[s2].parent=j;p[j].lchild=s1;p[j].rchild=s2;p[j].weight=p[s1].weight+p[s2].weight;}}void HuffmanCoding(HuffmanTree &HT,int n,char *w1,int*w2) {int m=2*n-1;if(n<=1) return;HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));HuffmanTree p=HT;for(int i=1;i<=n;i++){ p[i].data=w1[i-1];p[i].weight=w2[i];p[i].parent=p[i].lchild=p[i].rchild=0;}for(;i<=m;i++){ p[i].weight=p[i].parent=p[i].lchild=p[i].rchild=0; } Select(HT,n,m);ofstream outfile; //生成hfmTree文件outfile.open("hfmTree.txt",ios::out);for (i=1;i<=m;i++){outfile<<HT[i].weight<<"\t"<<HT[i].parent<<"\t"<<HT[i].lchild <<"\t"<<HT[i].rchild<<"\t"<<endl;}outfile.close();cout<<"初始化结果已保存在hfmTree文件中\n";}void ToBeTree() //将正文写入文件ToBeTree中{ofstream outfile;outfile.open("ToBeTree.txt",ios::out);outfile<<"THIS PROGRAM IS MYFA VORITE";outfile.close();}void Encoding(HuffmanTree &HT,int n) //编码{HuffmanCode HC;HC=(HuffmanCode)malloc((n+1)*sizeof(char *));char *cd;cd=(char *)malloc(n*sizeof(char));cd[n-1]='\0';for(int k=1;k<=n;k++){ int start=n-1;for(int c=k,f=HT[k].parent;f!=0;c=f,f=HT[f].parent){ if(HT[f].lchild==c) cd[--start]='0';else cd[--start]='1';}HC[k]=(char *)malloc((n-start)*sizeof(char));strcpy(HC[k],&cd[start]);}cout<<"输出哈夫曼编码:"<<endl;for(int h=1;h<=n;h++) //输出编码{ cout<<HT[h].data<<":";cout<<HC[h];cout<<" ";if (h%8==0) cout<<endl;}cout<<endl<<"输出正文编码:"<<endl;ToBeTree();//读取TOBETREE文件里的正文,并进行编码fstream infile;infile.open("ToBeTree.txt",ios::in);char s[80];while(!infile.eof()){infile.getline(s,sizeof(s));}infile.close();fstream outfile;outfile.open("CodeFile.txt",ios::out);int count=0;for (h=0;s[h]!='\0';h++){ for(k=1;k<=n;k++)if (s[h]==HT[k].data){ cout<<HC[k];cout<<" ";count++;outfile<<HC[k];break;}if (count%9==0) cout<<endl; //每输出7个换行}outfile.close();cout<<"\n编码结果已保存在文件CodeFile中.";cout<<endl;}void Decoding(HuffmanTree &HT,int n) //译码{int f=2*n-1;fstream infile;infile.open("CodeFile.txt",ios::in);char s[1000];while(!infile.eof()){infile.getline(s,sizeof(s));}infile.close();int i=0;int j=0;fstream outfile;outfile.open("TextFile.txt",ios::out);while(s[i]!='\0'){ f=2*n-1;while(HT[f].lchild!=0)//以f对应的节点的左孩子的值==0作为结束{if (s[j]=='0') f=HT[f].lchild;else f=HT[f].rchild;j++;}i=j;cout<<HT[f].data;outfile<<HT[f].data;}outfile.close();cout<<"\n译码结果已保存在文件TextFile中.";cout<<endl;}void Print() //印代码文件{ int count=0;fstream infile;infile.open("CodeFile.txt",ios::in);char s[1000];while(!infile.eof()){infile.getline(s,sizeof(s));for(int i=0;s[i]!='\0';i++){ cout<<s[i];count++;if (count%50==0) cout<<endl; //在终端上每行显示50个代码}}infile.close();cout<<endl;}char menu() //菜单函数{ cout<<"功能菜单如下:"<<endl;cout<<"* * * * * * * * * * * * * * * * * * * * *"<<endl; cout<<" I:初始化(Initialization) "<<endl;cout<<" E:编码(Encoding) "<<endl; cout<<" D:译码(Decoding) "<<endl; cout<<" P:印代码文件(Print) "<<endl; cout<<" Q:退出(Exit) "<<endl;cout<<"* * * * * * * * * * * * * * * * * * * * *"<<endl;cout<<"请输入功能字符:";char ch;cin>>ch;return ch;}void main(){ int n;int Array[100];char cArray[100];HuffmanTree HT;cout<<"输入n个字符:";cin.getline(cArray,100);n=strlen(cArray);cout<<"一共"<<n<<"个字符.\n";cout<<"依次输入各个字符的权值:"<<endl;for (int i=1;i<=n;i++) cin>>Array[i];int tag;char x=menu();while(1){ switch (x){case 'I':HuffmanCoding(HT,n,cArray,Array);break;case 'E':Encoding(HT,n);break;case 'D':Decoding(HT,n);break;case 'P':Print();break;case 'Q':tag=0;cout<<"结束"<<endl;break;default:cout<<"你输入错误!"<<endl;}if(tag==0) break;cout<<"y(继续) or n(退出)"<<endl;char ch;cin>>ch;if (ch=='y'){ cout<<"请输入功能字符:";char c;cin>>c;x=c;}else exit(1);}}测试数据:用下表给出的字符集和频度的实际统计数据建立哈夫曼树,并实现以下报文的译码和编码:"THIS PROGRAM IS MY FAVORITE".字符空格 A B C D E F G H I J K L M 频度186 64 13 22 32 103 21 15 47 57 1 5 32 20字符N O P Q R S T U V W X Y Z频度57 63 15 1 48 51 80 23 8 18 1 16 1四.测试结果:如图一所示五.实验体会通过本次实验,尤其在自己对程序的调试过程中,感觉对树的存储结构,终结状态,还有编码,译码的过程都有了比较清晰的认识。

相关文档
最新文档