哈夫曼编码.docx
《哈夫曼编码.docx》由会员分享,可在线阅读,更多相关《哈夫曼编码.docx(21页珍藏版)》请在冰豆网上搜索。
哈夫曼编码
课程设计任务书
2011—2012学年第1学期
电子与信息工程系专业班级
课程设计名称:
数据结构课程设计
设计题目:
哈夫曼编码
完成期限:
自2012年1月2日至2012年1月6日共1周
设计依据、要求及主要内容:
一、设计目的
熟悉各种数据结构和运算,会使用数据结构的基本操作解决一些实际问题。
二、设计要求
(1)重视课程设计环节,用严谨、科学和踏实的工作态度对待课程设计的每一项任务;
(2)按照课程设计的题目要求,独立地完成各项任务,严禁抄袭;凡发现抄袭,抄袭者与被抄袭者皆以零分计入本课程设计成绩。
凡发现实验报告或源程序雷同,涉及的全部人员皆以零分计入本课程设计成绩;
(3)学生在接受设计任务后,首先要按设计任务书的要求编写设计进程表;
(4)认真编写课程设计报告。
三、设计内容
设某编码系统共有n个字符,使用频率分别为{w1,w2,…,wn},设计一个不等长的编码方案,使得该编码系统的空间效率最好。
目录
一:
哈夫曼树4
二:
哈夫曼树的构造4
三:
哈夫曼编码4
四:
哈夫曼算法伪代码4
五:
需求分析:
5
六:
总体设计:
5
七:
详细设计7
八:
调试与测试8
九:
程序源代码8
十:
执行结果14
十一:
体会感想16
一:
哈夫曼树
给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffmantree)。
(1)路径和路径长度:
在一棵树中,从一个结点往下可以达到的孩子或子孙结点之间的通路,称为路径。
通路中分支的数目称为路径长度。
若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。
(2)结点的权及带权路径长度 若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。
结点的带权路径长度为:
从根结点到该结点之间的路径长度与该结点的权的乘积。
(3)树的带权路径长度 树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。
二:
哈夫曼树的构造
假设有n个权值,则构造出的哈夫曼树有n个叶子结点。
n个权值分别设为w1、w2、…、wn,则哈夫曼树的构造规则为:
(1)将w1、w2、…,wn看成是有n棵树的森林(每棵树仅有一个结点);
(2)在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;
(3)从森林中删除选取的两棵树,并将新树加入森林;
(4)重复
(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。
三:
哈夫曼编码
哈夫曼编码(HuffmanCoding)是一种编码方式,哈夫曼编码是可变字长编码(VLC)的一种。
Huffman于1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,一般就叫作Huffman编码。
四:
哈夫曼算法伪代码
1.数组huffTree初始化,所有元素结点的双亲、左右孩子都置为-1;
2.数组huffTree的前n个元素的权值置给定权值w[n];
3.进行n-1次合并
3.1在二叉树集合中选取两个权值最小的根结点,其下标分别为i1,i2;
3.2将二叉树i1、i2合并为一棵新的二叉树k;
五:
需求分析:
哈夫曼压缩是个无损的压缩算法,一般用来压缩文本和程序文件。
哈夫曼压缩属于可变代码长度算法一族。
意思是个体符号(例如,文本文件中的字符)用一个特定长度的位序列替代。
因此,在文件中出现频率高的符号,使用短的位序列,而那些很少出现的符号,则用较长的位序列。
程序功能及输入输出要求:
控制台输入需编码的字符个数,分别输入字符符号以及权值后,进行哈夫曼编码,将编码结果输出到txt文件中。
编码结束后,可在控制台输入0,1字符串进行解码,若解码成功,输出解码结果。
六:
总体设计:
(1):
编程软件MicrosoftVisualStudio2010
(2):
使用C++语言,自定义HuffmanCode类,在头文件HuffmanCode.h中。
如下如:
自定义类使用的字段,方法以及嵌套类型
图1:
HuffmanCode类的定义
(3):
HuffmanCode类的实现,在HuffmanCode.cpp文件中,如图
图2:
HuffmanCode类的实现
(4):
HuffmanCode主程序,在HuffmanCodeMain.cpp文件中,如图
图3:
主程序
七:
详细设计
如图HuffmanCode类中的公有成员:
HuffmanCode();主要进行初始化,申请所需内存空间,对字符命名及赋予权值;
HuffTree();由初始化后的信息构造哈夫曼树;
Huff_Encode();对字符进行哈夫曼编码;
Huff_Decode();对二进制信息进行哈夫曼解码;
HuffCode(string&CharacterCode);哈夫曼解码算法,供成员Huff_Decode()调用;
Ele_Path(intn,boolchild);供解码函数调用,返回下一个儿子结点所在下标;
~HuffmanCode();析构函数,释放内存空间
HuffmanCode类中的私有成员:
structElement{
stringname;
intflag;
TEle_Weight;
intl_child,r_child,parent;
};用于存放结点信息,即字符名称、标记、权值、左孩子、右孩子、双亲;
intNum;字符个数;
Element*Elements;一个指向Element结构体的指针;
string*Code;所需解码的字符串指针;
八:
调试与测试
(1):
n个字符进行哈夫曼编码,需2n-1个结点,在进行下标遍历时要注意,结点个数是2n-1但最后一个结点的下标是2n-2,否则地址出错,产生不可预测的结果;
(2):
执行解码时,由输入的0,1字符串判断下一个结点是左孩子还是右孩子,当浏览至叶子结点时,一定要把指针或者下表指向2n-1个结点(根节点)的位置,下表2n-2,否则会出错,导致解码失败;
(3):
执行n-1次合并时,参与过运算的结点一定要改变标记值,否则下次遍历会再次参与运算,导致结果错误。
九:
程序源代码
(1)HuffmanCode.h
#include
usingstd:
:
string;
//HuffmanCode.h声明类HuffmanCode
#ifndefHuffmanCode_H
#defineHuffmanCode_H
template
classHuffmanCode{
public:
HuffmanCode();//构造函数,HuffTree初始化
voidHuffTree();//构造哈夫曼树
voidHuff_Encode();//哈夫曼编码函数
voidHuff_Decode();//解码函数
voidHuffCode(string&CharacterCode);//解码算法
intEle_Path(intn,boolchild);//供解码函数嵌套调用,返回下一个儿子结点下标
//n代表当前结点下标,child代表下一结点所在下标
~HuffmanCode();//析构函数
private:
structElement{
stringname;//编码的字符
intflag;//标记
TEle_Weight;//权值
intl_child,r_child,parent;//左孩子,右孩子,双亲
};
intNum;
Element*Elements;
string*Code;//存放计算出字符哈夫曼编码
};
#endif
(2)HuffmanCode.cpp
#include"stdafx.h"
#include"HuffmanCode.h"
//无参构造函数,确定字符数目n,并初始化2*n-1个结点
template
HuffmanCode:
:
HuffmanCode(){
//输入一个大于2的整数
do{
cout<<"Pleaseinputthenumber(above2)ofcharacters:
";
cin>>Num;
}while(Num<2);
//新建2*Num-1个结点
Elements=newElement[2*Num-1];
for(inti=0;i<2*Num-1;i++){
Elements[i].name="";
Elements[i].Ele_Weight=0;
Elements[i].flag=0;
Elements[i].parent=-1;
Elements[i].l_child=-1;
Elements[i].r_child=-1;
}
for(inti=0;icout<<"PleaseinputthenameofNO."<
";
cin>>Elements[i].name;
cout<<"PleaseinputweightofNO."<
";
cin>>Elements[i].Ele_Weight;
}
//Textcout
cout<<"Thedatastructurebeforcomputeis:
"<cout<<<for(inti=0;i<2*Num-1;i++){
cout<cout<cout<cout<cout<cout<cout<}
}
template
voidHuffmanCode:
:
HuffTree(){
inti=0,
MIN_1,MIN_2,//最小与次小元素下标
MIN_W1,MIN_W2;//最小元素与次小元素的权值
for(i;iMIN_W1=MIN_W2=INT_MAX;//climits中定义的常量值
MIN_1=MIN_2=0;//下标0开始
//在森林中找两个权值最小的结点
for(intj=0;j是Num+i不是Num+1
if(Elements[j].flag==0){//该点未加入HuffmanTree中
if(Elements[j].Ele_WeightMIN_W2=MIN_W1;
MIN_2=MIN_1;
MIN_W1=Elements[j].Ele_Weight;
MIN_1=j;
}
elseif(Elements[j].Ele_WeightMIN_W2=Elements[j].Ele_Weight;
MIN_2=j;
}
}
}
//确定两个最小结点的双亲
Elements[MIN_1].parent=Num+i;
Elements[MIN_2].parent=Num+i;
//标记已加入HuffTree中
Elements[MIN_1].flag=1;
Elements[MIN_2].flag=1;
Elements[Num+i].Ele_Weight=Elements[MIN_1].Ele_Weight+Elements[MIN_2].Ele_Weight;
Elements[Num+i].l_child=MIN_1;
Elements[Num+i].r_child=MIN_2;
}
//Textcout
cout<<"Thedatastructurebeforcomputeis:
"<cout<<<for(inti=0;i<2*Num-1;i++){
cout<cout<cout<cout<cout<cout<cout<}
}
template
voidHuffmanCode:
:
Huff_Encode(){
//申请编码所需空间并初始化
Code=newstring[Num];
for(inti=0;iCode[i]="";
//计算第i个结点的HuffMan编码
TCode_Weight=0;
intchild=0;
intparent=0;
for(inti=0;iCode_Weight=Elements[i].Ele_Weight;
child=i;
parent=Elements[i].parent;//回溯
stringtemp;
while(parent!
=-1){
temp=(Elements[parent].l_child==child)?
"0":
"1";
Code[i].insert(0,temp);
child=parent;
parent=Elements[child].parent;
}
}
//输出结果
for(inti=0;icout<<"Thecharacter'sCode"<}
}
template
voidHuffmanCode:
:
Huff_Decode(){
//求字符编码最长的长度
string:
:
size_typeMAX_Length=0;
for(inti=0;icout<<"Element"<if(Code[i].size()>MAX_Length)
MAX_Length=Code[i].size();
}
cout<<"Themaximumlengthofcodeis"<//求字符编码最小的长度
string:
:
size_typeMIN_Length=MAX_Length;
for(inti=0;iif(Code[i].size()MIN_Length=Code[i].size();
}
cout<<"Theminimumlengthofcodeis"<//
stringDecode="";
do{//限制输入二进制代码长度
cout<<"PleaseinputyourCode\n(ithavetolongerthancharacters'maximumoflength):
\n";
//进行译码
HuffCode(Decode);
}while(cin>>Decode);
}
template
voidHuffmanCode:
:
HuffCode(string&CharacterCode){
stringtemp="";
intEle_Num=2*Num-2;//注意:
元素个数是2*Num-1,但根结点下标是2*Num-2,否则参数传递时地址出错
for(string:
:
size_typeLength=0;Lengthif(CharacterCode[Length]=='0'){//该字符为‘0’
Ele_Num=Ele_Path(Ele_Num,false);
if(Elements[Ele_Num].l_child==-1&&Elements[Ele_Num].r_child==-1){
temp+=Elements[Ele_Num].name;
Ele_Num=2*Num-2;//返回HuffmanTree的根结点
}
}
elseif(CharacterCode[Length]=='1'){//该字符为‘1’
Ele_Num=Ele_Path(Ele_Num,true);
if(Elements[Ele_Num].l_child==-1&&Elements[Ele_Num].r_child==-1){
temp+=Elements[Ele_Num].name;
Ele_Num=2*Num-2;//返回HuffmanTree的根结点
}
}
else{//输入编码中带有0,1以外的字符
cerr<<"Thecodehavebeenmadewrong."<Ele_Num=0;//这里Ele_Nun可以是任意一个!
=2*Num-2的值
break;
}
}
//译码输出
if(Ele_Num==2*Num-2)
cout<<"Code"<else
cerr<<"Code"<}
template
intHuffmanCode:
:
Ele_Path(intn,boolchild){
if(child==false){
returnElements[n].l_child;
}
else{
returnElements[n].r_child;
}
}
template
HuffmanCode:
:
~HuffmanCode(){
delete[]Elements;
Elements=NULL;
delete[]Code;
Code=NULL;
}
(3)HuffmanCodeMain.cpp
//HuffmanCodeMain.cpp:
定义控制台应用程序的入口点。
//
#include"stdafx.h"
#include
#include
#include
#include"HuffmanCode.cpp"
usingstd:
:
cin;
usingstd:
:
cerr;
usingstd:
:
cout;
usingstd:
:
endl;
usingstd:
:
setw;
int_tmain(intargc,_TCHAR*argv[])
{
HuffmanCodea;
a.HuffTree();
a.Huff_Encode();
a.Huff_Decode();
a.~HuffmanCode();
return0;
}
十:
执行结果
(1)如图4,运行主程序,输入字符个数及名称后,指向Element结构体的指针指向一段具有2n-1个结构体的连续内存空间;
图4:
输入字符个数及名称
(2)数据在内存中结构如图5
图5:
初始化字符个数及名称后的数据结构
(3)随后生成哈夫曼树,在内存中结构如图6:
图6:
生成后的哈夫曼树
(4):
由哈夫曼树对字符进行编码并输出编码长度等信息:
图7:
生成编码解决方案
输出编码结果到HuffmanCode.txt文件中,如图8;
图8:
输出编码结果到HuffmanCode.txt文件中
(5):
解码,若输入无误,则输出解码结果(注:
Ctrl+Z结束解码)
图9:
哈夫曼解码
十一:
体会感想
经过这次数据结构课程设计,使我更加深刻体会了数据结构在实际应用中的作用,有效地组织好数据结构,可以有效地使系统高效率地执行。
而且通过开始的类的设计,程序的流程设计,以及后来类的具体实现,更使我深刻了解了更多C++语言的精妙之处,而且经历多次的调试,更加知道了程序的执行流程。
由于这次课程设计很早就得到了自己需要做的题目,因此有很长的时间准备,C++中最重要的指针部分也是我这次调试中问题最多的,但是最终都修改正确。
4、参考文献
1.王红梅.数据结构(C++版).清华大学出版社
2.谭浩强.C++程序设计.清华大学出版社
3.王红梅.数据结构学习辅导与实验指导.清华大学出版社
4.StanleyB.LippmanJoseeLa