北邮数据结构实验三题目2哈夫曼树.docx
《北邮数据结构实验三题目2哈夫曼树.docx》由会员分享,可在线阅读,更多相关《北邮数据结构实验三题目2哈夫曼树.docx(19页珍藏版)》请在冰豆网上搜索。
北邮数据结构实验三题目2哈夫曼树
数据结构实验报告
实验名称:
实验三题目2哈夫曼树
学生姓名:
班级:
班内序号:
学号:
日期:
1.实验要求
实验目的:
Ø熟悉C++语言的基本编程方法,掌握集成编译环境的测试方法
Ø学习指针、模板类、异常处理的使用
Ø掌握线性表的操作实现方法
Ø培养使用线性表解决实际问题的能力
实验内容:
利用二叉树结构实现赫夫曼编/解码器。
基本要求:
1、初始化(Init):
能够对输入的任意长度的字符串s进行统计,统计每个字符的频度,并建立赫夫曼树
2、建立编码表(CreateTable):
利用已经建好的赫夫曼树进行编码,并将每个字符的编码输出。
3、编码(Encoding):
根据编码表对输入的字符串进行编码,并将编码后的字符串输出。
4、译码(Decoding):
利用已经建好的赫夫曼树对编码后的字符串进行译码,并输出译码结果。
5、打印(Print):
以直观的方式打印赫夫曼树(选作)
6、计算输入的字符串编码前和编码后的长度,并进行分析,讨论赫夫曼编码的压缩效果。
测试数据:
IlovedataStructure,IloveComputer。
IwilltrymybesttostudydataStructure.
提示:
1、用户界面可以设计为“菜单”方式:
能够进行交互。
2、根据输入的字符串中每个字符出现的次数统计频度,对没有出现的
字符一律不用编码。
2.程序分析
2.1存储结构
二叉树:
示意图:
root
lchildparentrchild
(静态三叉链表存储)
weight
LChild
RChild
parent
0
2
-1
-1
-1
1
3
-1
-1
-1
2
6
-1
-1
-1
3
9
-1
-1
-1
4
-1
-1
-1
5
-1
-1
-1
6
-1
-1
-1
(a)初始化哈夫曼树
weight
LChild
RChild
parent
0
2
-1
-1
4
1
3
-1
-1
4
2
6
-1
-1
5
3
9
-1
-1
6
4
5
0
1
5
5
11
4
2
6
6
20
3
5
-1
(b)创建好的哈夫曼树
哈夫曼编码的存储结构
data
code
0
Z
100
1
C
101
2
B
11
3
A
0
01
01
01
2.2关键算法分析
1初始化
算法步骤:
①统计字符串字符综述并申请动态数组存储字符串;
②对所存储字符串进行排序;
③统计不同字符的个数;
④释放动态内存空间;
⑤创建哈夫曼数的数组;
⑥创建哈夫曼编码表。
源代码:
voidHuffman:
:
Init(char*s)
{
intn=0;
while(*(s+n)!
='\0')
{
n++;//统计字符串字符数
}
char*temp=newchar[n];//申请动态数组存储字符串以便之后对字符串排序
for(inti=0;i{
temp[i]=*(s+i);
}
charctemp;
for(inti=0;i{
for(intj=0;j{
if(temp[j]>temp[j+1])
{
ctemp=temp[j];
temp[j]=temp[j+1];
temp[j+1]=ctemp;
}
}
}
intk=1;
for(inti=1;i{
if(temp[i]!
=temp[i-1])
{
k++;
}
}
HTree=newHNode[2*k-1];
m=2*k-1;//存储哈夫曼数组大小
intl=0;//统计不同字符出现的频度
k=0;//控制哈夫曼数组下标
ctemp=temp[0];//做标记
for(inti=0;i{
if(temp[i]==ctemp)
{
l++;//统计不同字符出现的频度
if(i==n-1)
HTree[k].weight=l;
}
else
{
HTree[k].c=ctemp;
HTree[k].weight=l;
ctemp=temp[i];
k++;
l=1;
HTree[k].c=ctemp;
HTree[k].weight=l;
}
}
delete[]temp;//释放动态内存空间
Creat();//创建哈夫曼数的数组
CodeTable();//创建哈夫曼编码表
}
时间复杂度:
n
2创建哈夫曼树动态数组
算法步骤:
①根据权重数组初始化哈夫曼树;
②选取权值最小的两个结点作为左右子树,构成一个新的二叉树,其根结点的权值取左右子树权值之和;
③删除这两棵子树,将新构成的树加入到T中,重复②③直至T中只含有一棵树。
源代码:
voidHuffman:
:
Creat()
{
intn=(m+1)/2;//n表示不同字符的个数
for(inti=0;i{
HTree[i].LChild=-1;
HTree[i].RChild=-1;
HTree[i].parent=-1;
}
intx,y;
for(inti=n;i<2*n-1;i++)
{
SelectMin(x,y,i);//找出最小权重的两个字符
HTree[x].parent=i;
HTree[y].parent=i;
HTree[i].weight=HTree[x].weight+HTree[y].weight;
HTree[i].LChild=x;
HTree[i].RChild=y;
HTree[i].parent=-1;
}
}
时间复杂度:
n
3寻找权值最小的两个数
算法步骤:
①首先判断根结点是否是最小,若不是,则继续判断;
②依次对各个结点进行判断,找出最小结点;
③找出最小权重结点后将最小节点放置在无法再次访问的位置,再进行次小值的判断。
源代码:
voidHuffman:
:
SelectMin(int&x,int&y,inti)
{
x=0;
while(HTree[x].parent!
=-1)//防止第一个就是之前找到的最小的导致下面循环无效
{
x++;
}
for(intj=1;j
{
if(HTree[j].parent!
=-1)
{
continue;
}
x=(HTree[x].weight<=HTree[j].weight)?
x:
j;
}
HTree[x].parent=-2;//防止y重复
y=0;
while(HTree[y].parent!
=-1)
{
y++;
}
for(intj=1;j
{
if(HTree[j].parent!
=-1)
{
continue;
}
y=(HTree[y].weight<=HTree[j].weight)?
y:
j;
}
}
时间复杂度:
n
4生成哈夫曼编码表
算法步骤:
①申请与不同字符个数对应的动态空间,以存储不同字符代表的编码;
②自下而上判断,左孩子则编码'0',右孩子则编码'1',到达根节点之后结束循环;
③字符编码串最后添加结束符,结束编码;
④颠倒字符串。
源代码:
voidHuffman:
:
CodeTable()
{
intn=(m+1)/2;
HcodeTable=newHCode[n];//申请与不同字符个数对应的动态空间,以存储不同字符代表的编码
for(inti=0;i{
HcodeTable[i].data=HTree[i].c;
intchild=i;
intparent=HTree[i].parent;//用于判断
intk=0;
while(parent!
=-1)//自下而上到达根节点之后结束循环
{
if(child==HTree[parent].LChild)//左孩子则编码'0'
HcodeTable[i].code[k]='0';
else
HcodeTable[i].code[k]='1';//右孩子则编码'1'
k++;
child=parent;
parent=HTree[child].parent;
}
HcodeTable[i].code[k]='\0';//字符编码串最后添加结束符
Reverse(HcodeTable[i].code);//由于自上而下因此需要颠倒字符串
}
}
时间复杂度:
n
5颠倒字符串
算法步骤:
①统计字符串字符个数;
②将第i个字符与第n-i个进行交换。
voidHuffman:
:
Reverse(charc[])
{
intn=0;
chartemp;
while(c[n+1]!
='\0')//统计字符个数
{
n++;
}
for(inti=0;i<=n/2;i++)
{
temp=c[i];
c[i]=c[n-i];
c[n-i]=temp;
}
}
时间复杂度:
n
6编码函数
算法步骤:
①将输入的字符串与编码表中的比较,字符串尾停止,如果相等,则对字符进行编码,并对每个字符的编码进行输出;
②编码的同时统计编码的总字节数;
③计算哈夫曼编码压缩比。
voidHuffman:
:
Encode(char*s,string*d)
{
floatsum=0;//统计字节数
intn=0;
while(*s!
='\0')
{
for(inti=0;i<(m+1)/2;i++)
{
if(*s==HTree[i].c)
{
cout<for(intj=0;HcodeTable[i].code[j]!
='\0';j++)
{
*d+=HcodeTable[i].code[j];
sum+=1;
}
s++;
n++;
break;
}
}
}
cout<<"压缩比为:
"<}
时间复杂度:
n
7解码函数
算法步骤:
①从根节点开始进行解码,如果字符为0,则为左孩子,1,则为右孩子;
②编码匹配编码表中对应的数据,进行解码。
源代码:
voidHuffman:
:
Decode(char*s,char*d)
{
while(*s!
='\0')
{
intparent=m-1;//根结点
while(HTree[parent].LChild!
=-1)//从根节点开始直到终端结点结束循环
{
if(*s=='0')
parent=HTree[parent].LChild;
else
parent=HTree[parent].RChild;
if(*s=='\0')//不解码码串最尾部,没有编码的部分
{
return;
}
s++;
}
*d=HcodeTable[parent].data;
d++;
}
}
时间复杂度:
n
2.3其他
1判断最小权值的函数还可以简化为一个循环;
2没有直观地画出哈夫曼树,用户只能通过哈夫曼编码表进行解码。
3.程序运行结果
1、测试主函数流程
3
12
2测试结果如图
4.总结
1、出现的问题及解决办法
编写最小权值函数时,寻找到的最小值是一样的,原因是寻找次小值的过程中,重复了寻找最小值的过程,造成重复,之后在寻找到最小值后将其父结点更改,使接下来的寻找过程不再考虑它
2、心得体会
进一步了解哈夫曼树的思想和相关概念,锻炼了使用二叉树解决实际问题的能力
3、下一步的改进
将查找权值最小的两个数的函数由2个循环改进为一个循环,简便算法;
直观地画出哈夫曼树,使用户更加清晰地认识编码结构。