北邮数据结构实验报告三题目2哈夫曼树.docx
《北邮数据结构实验报告三题目2哈夫曼树.docx》由会员分享,可在线阅读,更多相关《北邮数据结构实验报告三题目2哈夫曼树.docx(12页珍藏版)》请在冰豆网上搜索。
北邮数据结构实验报告三题目2哈夫曼树
1.实验要求
利用二叉树结构实现哈夫曼编/解码器
(1).初始化:
能够对输入的任意长度的字符串s进行统计,统计每个字符的频度,并建立哈夫曼树。
(2).建立编码表:
利用已经建好的哈夫曼树进行编码,并将每个字符的编码输出。
(3).编码:
根据编码表对输入的字符串进行编码,并将编码后的字符串输出。
(4).译码:
利用已经建好的哈夫曼树对编码后的字符串进行译码,并输出译码结果。
(5).打印:
以直观的方式打印哈夫曼树。
(6).计算输入的字符串编码前和编码后的长度,并进行分析,讨论哈夫曼编码的压缩效果。
(7).用户界面可以设计成“菜单”方式,能进行交互,根据输入的字符串中每个字符出现的次数统计频度,对没有出现的字符一律不用编码。
2.程序分析
2.1存储结构
二叉树:
示意图:
root
2.2
2.3关键算法分析
1.定义哈夫曼树的模板类
#include#includeusingnamespacestd;structLNode{
charch;
intweight;
charcode[20];
LNode*next;};
structTNode
{
intweight;//
intLchild;//
intRchild;//
intParent;//
};
classHuffman
结点权值
左孩子指针右孩子指针双亲指针
//链表的节点,用来统计字符频率,并编码
//字符
//权值
//字符编码
//指向下一个节点
//哈弗曼树结点的结构体
public:
Huffman();~Huffman();voidCreateTable();voidPrintTable();voidEncoding();voidDecoding();voidComrate();
//构造函数,输入、统计字符,创建哈弗曼树、码表
//释放链表空间、哈弗曼树的节点
//建立编码表,并将信息存入链表
//输出码表
//哈弗曼编码
//译码
//计算编码的压缩率
voidSelectMin(int&x,int&y,intbegin,intend);
voidreverse(charch[]);voidcontrol();
private:
//选取权值最小的两个数,创建哈弗曼树
//将码值倒置,用来编码
//对菜单交互等提示操作
TNode*troot;
LNode*lroot;voidList(chara[]);voidHTree();intLetter;charastr[1000];charbstr[1000];
//在统计字符频率是构建链表的根节点
//统计字符的权值建立的单链表
//哈弗曼树建立
//共有不同字符总数
//用户输入的一串字符
//将字符串的码值保存
Huffman:
:
Huffman()
{
lroot=newLNode;
bstr[0]='\0';
lroot->next=NULL;
Letter=0;//初始化字符总数为1cout<<"请输入一串字符,以回车键结束"<if(strlen(astr)==0)throw1;
else
{
List(astr);//用链表存储每个字符
HTree();
CreateTable();
Encoding();
}
};
Huffman:
:
~Huffman()
{
deletetroot;
LNode*p=lroot;
while(p=lroot->next)
个结点
//建立叶子节点
//是两个最小值的角标
{lroot=p->next;deletep;p=lroot;
}
deletep;
};
2.建立哈夫曼树
voidHuffman:
:
HTree()
{
LNode*p=lroot;
inta=0;
troot=newTNode[2*Letter-1];//2n-1
while(p=p->next)
{
troot[a].weight=p->weight;
troot[a].Parent=-1;
troot[a].Lchild=-1;
troot[a].Rchild=-1;
a++;
};
for(inti=Letter;i<2*Letter-1;i++)
troot[i].Parent=-1;
intx,y,begin=0;
for(intj=Letter;j<2*Letter-1;j++)
while(troot[begin].Parent!
=-1)
begin++;
SelectMin(x,y,begin,j);
troot[j].weight=troot[x].weight+troot[y].weight;
troot[j].Lchild=x;
troot[j].Rchild=y;
troot[j].Parent=-1;
troot[x].Parent=j;
troot[y].Parent=j;
}
};
3.统计字符的频率
voidHuffman:
:
List(chara[])
{
LNode*p=lroot;
inti=0;
while(a[i]!
='\0')
{
while(p&&p->ch!
=a[i])//查找链表中没有该字符或者找到该字符
p=p->next;
if(!
p)//如果没有该字符,创建节点。
p=newLNode;
p->ch=a[i];
p->weight=1;
p->next=lroot->next;
lroot->next=p;
Letter++;
}
else
p->weight++;
i++;
p=lroot->next;
};
};
4.选最小值
voidHuffman:
:
SelectMin(int&x,int&y,intbegin,intend)
{
b开始比较
intt1,b,t2;//分别表示临时最小值、对应角标、从
t1=troot[begin].weight,b=t2=begin;
for(;b{
if(troot[b].weightt1=troot[b].weight;
t2=b;
}
}
x=t2;
防止再次被找出
troot[x].Parent=100;//临时为该最小的双亲赋值,
if(t2!
=begin)//判断最小是否是第一个
b=begin;
else
{
b=begin;
while(troot[++b].Parent!
=-1);
}
t1=troot[b].weight;t2=b;
for(;b{
if(troot[b].weight{
t1=troot[b].weight;
t2=b;
y=t2;
};
5.倒置字符串
voidHuffman:
:
reverse(charch[])
{
for(inti=0;i{
chartemp=ch[i];
ch[i]=ch[strlen(ch)-i-1];
ch[strlen(ch)-i-1]=temp;
}
6.建立码表,并将信息存在链表
voidHuffman:
:
CreateTable()
{
LNode*p=lroot;
inti=0,j,k;//将letter个字符都编码,与p相对应
while(p=p->next)
{
j=i,k=0;
while(troot[j].Parent!
=-1)
{
if(troot[troot[j].Parent].Lchild==j)p->code[k++]='0';
else
p->code[k++]='1';j=troot[j].Parent;
}p->code[k]='\0';reverse(p->code);
i++;
}
7.打印码表和编码结果
voidHuffman:
:
PrintTable()
{
LNode*p=lroot;
cout<<"字符:
频率:
编码:
\n";
while(p=p->next)
{cout<ch<<"\t"<weight<<"\t"<code<};
cout<<"原字符编码结果为:
"<}
8.编码
voidHuffman:
:
Encoding()
{
intk=0;//输入字符串的脚标
LNode*p;
while(astr[k]!
='\0')//所有字符编码完为止
{
p=lroot;
while((p=p->next)->ch!
=astr[k]);//找到字符的码值为止strcat(bstr,p->code);
k++;
}
}
9.解码
voidHuffman:
:
Decoding()
{
cout<<"对码值"<\t";
intk=0,parent=2*Letter-2;
while(bstr[k]!
='\0')//码值读取结束
{
if(bstr[k]=='0')
parent=troot[parent].Lchild;else
parent=troot[parent].Rchild;if(troot[parent].Lchild==-1){
LNode*p=lroot;
for(inti=0;inext;
cout<ch;
parent=2*Letter-2;
}
k++;
}cout<}
10.计算压缩结果
voidHuffman:
:
Comrate()
{
cout<<"编码前大小:
"<"<cout<<"压缩率为:
"<<100*float(strlen(bstr))/strlen(astr)/8<<"%"<}
11.控制
voidHuffman:
:
control()
{
intm;
while
(1)
{
cout<<"请选择执行功能:
\n";
cout<<"1.打印编码表\n2.原始数据解码\n3.数据大小及压缩率\n4.结束\n\n";
cin>>m;switch(m){case1:
{cout<<"打印编码表\n";PrintTable();break;
}
case2:
{cout<<"原始数据解码\n";Decoding();break;
}
case3:
{
cout<<"数据大小及压缩率\n";Comrate();
break;
case4:
cout<<"欢迎使用\n";
}
}
};
3.程序运行结果分析
4.
4.选择1,打印编码表
1.哈夫曼树初始化成功
5.
选择2,解码
6.选择3,计算压缩结果
7.选择4,退出
5.总结
5.1实验的难点和关键点
调试出现的问题:
键入的字符为空
解决办法:
用throw和catch语句来排除异常
5.2心得体会
经过这次实验,我了解了哈夫曼树的创建过程,了解了一种不等长编码的方法,用设断点调试的方法更加熟练,同时熟悉了STL中string类型的用法,对C++更加熟悉