Huffman编解码问题讲解.docx
- 文档编号:10022201
- 上传时间:2023-02-08
- 格式:DOCX
- 页数:10
- 大小:63.13KB
Huffman编解码问题讲解.docx
《Huffman编解码问题讲解.docx》由会员分享,可在线阅读,更多相关《Huffman编解码问题讲解.docx(10页珍藏版)》请在冰豆网上搜索。
Huffman编解码问题讲解
Huffman编解码问题——讲解
2.5Huffman编码问题
实验四——题目2:
利用二叉树结构实现哈夫曼编/解码器。
基本要求:
1、初始化(lnit):
能够对输入的任意长度的字符串s进行统计,统计每个字符的频度,并建立哈夫曼树
2、建立编码表(CreateTable):
利用己经建好的哈夫曼树进行编码,并将每个字符的编码输出。
3、编码(Encoding):
根据编码表对输入的字符串进行编码,并将编码后的字符串输
出。
4、译码(Decoding):
利用己经建好的哈夫曼树对编码后的字符串进行译码,并输出译码结果。
5、打印(Print):
以直观的方式打印哈夫曼树(选作)
6、计算输入的字符串编码前和编码后的长度,并进行分析,讨论赫夫曼编码的压缩效果。
7、可采用二进制编码方式(选作)
实验讲解:
Huffman编解码的实验按照模块化分,可以划分成如下部分:
a)统计输入的字符串中字符频率
b)创建Huffman树
c)打印Huffman树
d)创建Huffman编码表
e)对输入的字符串进行编码并输出编码结果
f)对编码结果进行解码,并输出解码后的字符串
g)最后编写测试函数,测试上述步骤的正确性。
根据模块化分,设计Huffman的存储结构如下:
1)Huffman树的结点结构structHNode{
intweight;//结点权值
intparent;//双亲指针
intLChild;//左孩子指针
intRChild;//右孩子指针
};
2)编码表结点结构(如右图2-6所示)
structHCode{
chardata;charcode[100];
};
图2-6Huffman树编码结构
3)Huffman类结构
classHuffman
{
intn;//叶子节点数
voidinit();//初始化
voidCreateHTree();//创建huffman树
voidSelectMin(int&x,int&y,ints,inte);
voidCreateCodeTable();//创建编码表
voidEncode(char*d);//编码
voidDecode(char*s,char*d);//解码
voidprint(inti,intm);//打印Huffman树
〜Huffman();
}
根据实验要求,分步骤实现如下:
步骤1:
统计输入的字符串中字符频率
Huffman编码的第一步需要使用字符出现的频率作为输入,本实验使用从键盘输入的方式进行,需要的解决得问题有2个:
一是输入的字符串中间有空格如何处理?
二是如何使统计效率更高?
例如:
charstr[1024];cin>>str;
上述代码运行后输入字符串,但cm>>str遇到空格就停止本次读取,所以我们需要使用其它的方法来进行输入,即需要使用cm.get()函数进行字符串读取。
get()方法每调用一次,读取一个字符,该字符的ASCII码作为返回值返回,换行回车等控制字符也当作普通字符
进行读取,因此需要指定结束读取的标志字符,才能停止get()函数的循环调用。
本实验中可以将字符读取和统计结合在一起进行。
示例代码如下:
1
//记录每一个字符出现的次数
//统计字符出现的次数//记录原始字符串//读取下一个字符
intnNum[256]={0};
2intch=cin.get();
3inti=0;
4while((ch!
=V)&&(ch!
=’\n’)){
5nNum[ch]++;
6str[i++]=ch;
7ch=cin.get();
}
8str[i]=,\0’
其中,整型数组变量nNum用来记录每一个字符出现的次数(若该字符未出现,则对应的nNum[ch]的值为0),可以把读取的字符ch的ASCII码当成,当ch出现时,nNum[ch]自
动加一。
当然,数组nNum中的等于零的字符会有很多,不方便后续hufman树的创建,因此可
以进行过滤,仅留下出现次数大于零的字符。
因此,完整的初始化代码如下:
voidHuffman:
:
init()
{
①〜⑧
n=0;
for(i=0;i<256;i++)
{
if(nNum[i]>0)//若nNum[i]==0说明该字符未出现
{
leaf[n]=(char)i;a[n]=nNum[i];
n++;
}
}
}
其中,数组leaf存储出现次数大于零的字符,相应的数组a存储该字符出现的次数,n为字符数,作为步骤2创建Huffman树的输入。
字符数组str存储用户输入的字符串,作为步骤5编码的输入。
当然,也可以使用其它方法进行字符的统计,请读者自行思考。
步骤2:
创建Huffman树
该步骤在教材5.4.2小节中进行了详细的讲解和实现,其中有一个选择权值之中最小的两个权值的函数,即函数SelectMin(int&x,int&y,ints,inte);其中x为最小权值,y为次小权
值,s为权值范围的起始下标,e为结束下标。
该函数如何实现呢?
分析:
从所有未使用过的权值表中选择两个最小的权值,可以有多种方法,比如一次选择一个最小的,选择2遍;或者进行迭代,一次选择出两个。
显然,后者的时间效率较高,因此我们采用后者进行实现。
迭代选择两个最小值得基本思想是:
1、从权值表HTree[s..e]中选取第一个未使用结点下标为x,并设y=x;
2、从剩下的未使用的权值中依次遍历
若当前结点i的权值<结点x的权值,则迭代,即y=x;x=i;
否则:
若此时y=x(即y还未赋值),则y=i;
若此时当前结点i的权值 具体实现如下: voidHuffman: : SelectMin(int&x,int&y,ints,inte) { inti; for(i=s;i<=e;i++) if(HTree[i].parent==-1) { x=y=i;break;//找出第一个有效权值x,并令y=x } for(;i if(HTree[i].parent==-1)//该权值未使用过 { if(HTree[i].weight { y=x;x=i;//迭代,依次找出前两个最小值 } elseif((x==y)||(HTree[i].weight y=i;//找出第2个有效权值y } } 特别说明,本例中叶子节点数n作为成员变量,因此,huffman类的成员函数的参数中不必在添加intn这个参数,直接使用n即可。 步骤3: 打印Huffman树 Huffman树的直观表示方式由多种,我们常见的树状结构如图所示是其中的一种,此外还有如图a所示的嵌套集合表示法,如图b所示的广义表表示法和图c所示的凹入表示法。 图-7树型表示法 图-8其他表示法 树型表示法当结点很多的时候,不容易打印的非常合适,所以我们可以选择使用凹入表的方式打印任意形状的Huffman树。 根结点空一格直接打印,第2层结点空2格打印,第3层结点空3格的打印,以此类推,每个节点占用独立的一行。 由于只有叶子结点是有对应字符的,所以其他结点可以打印该结点的权值。 因此,我们可以尝试使用二叉树前序遍历的方式来进行直观的打印。 示例代码如下: #defineN10//定义树的最大深度 voidHuffman: : print(inti,intm) { if(HTree[i].LChild==-1) cout< else { cout< } } 其中,参数i表示Huffman树的下标为i的结点,m表示该结点的层次。 该函数是递归函数,所以在main()函数中第一次调用该函数时,实参为i=2*n-2,m=1; 步骤4: 创建编码表 该步骤请参考教材5.4.2小节中的讲解和实现即可。 步骤5: 编码 编码表生成后,进行编码相对容易,实验要求只要能够显示出来编码后的字符串即可,也就是说,若A的编码为0,B的编码为10,则字符串AAB的编码显示为"0010"即可。 由于初始 化函数中己经记录了输入的字符串str,因此直接使用该变量作为输入即可。 voidHuffman: : Encode(char*d) { char*s=str;while(*s! =’\0’) { for(inti=0;i if(*s==HCodeTable[i].data) { ①strcat(d,HCodeTable[i].code);//d为编码后的字符串 break; } s++; } } 上述代码用于本实验的编码显示和验证是没问题的,但并没有实现真正的压缩效果,这时因为代码①的实现。 比如若A的编码为100,实际压缩中使用3个bit代替字符A,本例中使用了3个字符“100”来编码,因此没有真正的压缩效果。 如果希望能够按照bit的方式进行编码,需要使用位运算符进行bit的操作,将编码按照bit的方式写入文件。 请同学们自行思考,如何采用bit的方式使用Huffman编码压缩文件。 步骤6: 解码 该步骤请参考教材5.4.2小节中的讲解和实现即可。 步骤7: 测试 根据测试数据,编写如下的测试man()函数进行测试: voidmain() { HuffmanHFCode; cout<<"请输入要编码的字符串: "; HFCode.init(); cout<<"创建Huffman树: "< HFCode.CreateHTree(); HFCode.print(2*HFCode.n-2,1); cout<<”创建Huffman编码表: ’’< chard[1024]={0}; HFCode.Encode(d); cout<<"编码结果: "< chars[1024]={0}; HFCode.Decode(d,s);cout<<”解码结果: "< } 最后,也是特别要注意的地方一一内存泄露。 本实验中的主要数据结构HTree和HCodeTable都是动态内存,因此必须要在Huffman树的析构函数中进行内存清理,示例代码如下: Huffman: : ~Huffman() { delete[]HTree;delete[]HCodeTable; } 本实验的运行效果如图所示。 图2-9运行测试结果 根据要求,我们下面讨论一下Huffman编码的压缩效果。 数据压缩比(英文名称: datacompressionratio)为衡量数据压缩器压缩效率的质量指标。 是指数据被压缩的比例。 其计算公式如下: 压缩比=压缩前字节数/压缩后字节数 本实验为了方便显示和验证算法,采用的是字符串方式进行压缩,即若一个字符A的Huffman编码为“010”,采用3个字符进行编码显示,即一个字节的字符A用了3个字节的字符编码。 实际中应当采用3个bit进行压缩,才能有压缩效果。 因此,按照图中的测试数据,本实验Huffman编码的压缩比为: 15/18=0.83;实际按照bit压缩的压缩比为: 15*8/18=6.67。 Huffman编解码的实验是一个比较综合的实验,在实验过程中要体会如何程序结构设计,学习对大量代码的程序组织能力,从而奠定对未来大项目的设计和实现基础。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Huffman 解码 问题 讲解