哈夫曼编码/译码系统(树应用)
[问题描述]
利用哈夫曼编码进行通信,可以压缩通信的数据量,提高传输效率,缩短信息的传输时间,还有一定的保密性。现在要求编写一程序模拟传输过程,实现在发送前将要发送的字符信息进行编码,然后进行发送,接收后将传来的数据进行译码,即将信息还原成发送前的字符信息。
[实现提示]
在本例中设置发送者和接受者两个功能,
发送者的功能包括:
①输入待传送的字符信息;
②统计字符信息中出现的字符种类数和各字符出现的次数(频率);
②根据字符的种类数和各自出现的次数建立哈夫曼树;
③利用以上哈夫曼树求出各字符的哈夫曼编码;
④将字符信息转换成对应的编码信息进行传送。
接受者的功能包括:
①接收发送者传送来的编码信息;
②利用上述哈夫曼树对编码信息进行翻译,即将编码信息还原成发送前的字符信息。
从以上分析可发现,在本例中的主要算法有三个:
(1)哈夫曼树的建立;
(2)哈夫曼编码的生成;
(3)对编码信息的翻译。
[设计思路]
- 哈夫曼树的建立
- 哈夫曼编码的生成
- 对编码信息的翻译
[代码及注释]
#include<iostream>
#include<map>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXSIZE 111 /*可传递信息的最大长度*/
//结构
typedef struct{
int weight; /*结点的权值*/
int parent,lchild,rchild; /*结点的双亲、左孩子、右孩子的下标*/
char ch; /*结点的字符*/
string HuffmanCode; /*结点的哈夫曼编码*/
}HTNode,*HuffmanTree; /*动态分配数组储存哈夫曼树*/
map<char,int> Map; /*map容器储存字符及对应次数*/
//子函数
void GetInformation(int &kind); /*统计字符种类数和各字符出现的次数*/
void SelectMinNode(HuffmanTree &HT,int k,int &s1,int &s2);/*选择两个权值最小结点*/
bool CreatHuffmanTree(HuffmanTree &HT,int n); /*哈夫曼树的创建*/
void CreatHuffmanCode(HuffmanTree &HT,int n); /*哈夫曼编码的创建*/
void Translate(HuffmanTree HT,int n); /*译码*/
//具体内容
void GetInformation(int &kind)
{
kind=0;
cout<<"输入传递信息(不含中文):";
char str[MAXSIZE];
gets(str); /*可读入含空格道德字符串*/
for(int i=0;i<strlen(str);++i)
Map[str[i]]++; /*记录字符出现次数*/
map<char,int> ::iterator it=Map.begin();
while(it!=Map.end())
{
++kind;
it++;
} /*迭代器遍历*/
cout<<endl;
cout<<"字符种类:"<<kind<<endl<<endl;
cout<<" ---------"<<endl;
cout<<"|字符"<<" 次数"<<endl;
for(it=Map.begin();it!=Map.end();++it)
cout<<"| "<<(*it).first<<" "<<(*it).second<<endl;
cout<<" ---------"<<endl;
}
void SelectMinNode(HuffmanTree &HT,int k,int &s1,int &s2)
{
int Min=1e9; /*初始化当前最小权值*/
for(int i=1;i<=k;++i)
if(!HT[i].parent)
if(HT[i].weight<=Min)
{
Min=HT[i].weight; /*不断更新Min*/
s1=i;
} /*从前k个结点找出最小结点*/
Min=1e9; /*重置权值*/
for(int i=1;i<=k;++i)
if(!HT[i].parent&&i!=s1)
if(HT[i].weight<=Min)
{
Min=HT[i].weight; /*不断更新Min*/
s2=i;
} /*从前k个结点除s1外找出最小结点*/
}
bool CreatHuffmanTree(HuffmanTree &HT,int n)
{
if(n<=1)
{
cout<<"<<<<字符种类太少无需使用哈夫曼树"<<endl;
return false;
}
else
{
int m=2*n-1;
HT=new HTNode[m+1]; /*分配空间,0号单元未使用*/
map<char,int> ::iterator it=Map.begin();
int j=0;
while(it!=Map.end())
{
++j;
HT[j].weight=(*it).second; /*初始化字符出现次数*/
HT[j].ch=(*it).first; /*初始化字符*/
HT[j].HuffmanCode=""; /*初始化哈夫曼编码为空*/
HT[j].lchild=HT[j].rchild=HT[j].parent=0; /*左右孩子及双亲均初始化为0*/
++it;
}
for(int i=n+1;i<=m;++i)
HT[i].weight=HT[i].parent=HT[i].rchild=HT[i].lchild=0;
/*初始化从n+1到m的结点*/
for(int i=n+1;i<=m;++i) /*n-1次的选择删除合并来创建哈夫曼树*/
{
int s1,s2;
SelectMinNode(HT,i-1,s1,s2); /*将最小两个结点的下标赋予s1,s2;*/
HT[s1].parent=i; /*s1双亲亲赋予i第i编号*/
HT[s2].parent=i; /*s2双亲亲赋予i第i编号*/
HT[i].lchild=s1; /*s1成为编号为i的左孩子*/
HT[i].rchild=s2; /*s2成为编号为i的右孩子*/
HT[i].weight=HT[s1].weight+HT[s2].weight; /*合并左右孩子权值*/
}
cout<<"<<<<哈夫曼树创建成功"<<endl<<endl;
return true;
}
}
void CreatHuffmanCode(HuffmanTree &HT,int n)
{
int c,f;
cout<<"<<<<哈夫曼编码创建成功,如下:"<<endl;
for(int i=1;i<=n;i++)
{
c=i; /*c:当前结点序号*/
f=HT[i].parent; /*f:当前节点的双亲*/
while(f!=0) /*f为根节点终止*/
{
if(HT[f].lchild==c)HT[i].HuffmanCode+="0"; /*左边加0*/
else HT[i].HuffmanCode+="1"; /*右边加1*/
c=f;
f=HT[f].parent;
/*不断向上层查找*/
}
reverse(HT[i].HuffmanCode.begin(),HT[i].HuffmanCode.end());
/*翻转,因为上步操作得到的是逆序*/
cout<<HT[i].ch<<":"<<HT[i].HuffmanCode<<endl;
}
cout<<endl;
}
void Translate(HuffmanTree HT,int n)
{
cout<<"输入译码:";
string str;
cin>>str;
int len=str.size();
int flag=-1; /*判断译码是否成功的标志*/
string ans,temp; /*ans:最终译码答案*/
/*temp:匹配临时字符串*/
for(int i=0;i<len;++i)
{
temp+=str[i];
for(int j=1;j<=n;++j)
if(HT[j].HuffmanCode==temp)
{
temp=""; /*匹配成功将临时字符串置空*/
ans+=HT[j].ch;
flag=i;
}
}
if(flag!=len-1)cout<<"<<<<输入的源码有误!!!"<<endl; /*没有匹配成功*/
else
{ cout<<"<<<<译码成功!内容如下"<<endl;
cout<<ans<<endl;
}
cout<<"是否继续(y/n)?";
char judge; /*判断是否继续译码*/
cin>>judge;
if(judge=='y')Translate(HT,n);
else return;
}
int main()
{
HuffmanTree HT;
int kind;
GetInformation(kind);
if(CreatHuffmanTree(HT,kind))
{
CreatHuffmanCode(HT,kind);
Translate(HT,kind);
}
return 0;
}
[简单展示]