哈夫曼编码/译码系统(树应用)
[问题描述]
利用哈夫曼编码进行通信,可以压缩通信的数据量,提高传输效率,缩短信息的传输时间,还有一定的保密性。现在要求编写一程序模拟传输过程,实现在发送前将要发送的字符信息进行编码,然后进行发送,接收后将传来的数据进行译码,即将信息还原成发送前的字符信息。
[实现提示]
在本例中设置发送者和接受者两个功能,
发送者的功能包括:
①输入待传送的字符信息;
②统计字符信息中出现的字符种类数和各字符出现的次数(频率);
②根据字符的种类数和各自出现的次数建立哈夫曼树;
③利用以上哈夫曼树求出各字符的哈夫曼编码;
④将字符信息转换成对应的编码信息进行传送。
接受者的功能包括:
①接收发送者传送来的编码信息;
②利用上述哈夫曼树对编码信息进行翻译,即将编码信息还原成发送前的字符信息。
从以上分析可发现,在本例中的主要算法有三个:
(1)哈夫曼树的建立;
(2)哈夫曼编码的生成;
(3)对编码信息的翻译。
分析:输进去一个字符串的时候,先统计每一个字符出现的次数,即权值,然后统计出现的字符种类数,接下来就是建树过程,详见代码,统计编码的时候用结构体倒着保存,但是这样解码的时候对比不方便,因此用map映射将编码和字符建立联系,而且只用了两种映射关系:字符对编码、编码对字符。译码的时候在这个映射关系里面寻找对应的字符,若最后没有找到对应的字符,则译码失败。
源码:
#include<iostream>
#include<stdio.h>
#include<string>
#include<string.h>
#include<cstring>
#include<stack>
#include<queue>
#include<algorithm>
#include<math.h>
#include<vector>
#include<iomanip>
#include<map>
using namespace std;
#define MAXVALUE 10000
#define MAXLEAF 30
#define MAXBIT 100
int n;
string str;
map<string,string>bianma1;
map<char,string>bianma2;
typedef struct
{
int bit[MAXBIT];
int start;
} HCodeType;
typedef struct
{
char ch;
int weight;
int parent,lchild,rchild;
}HNodeType;
void HaffmanTree(HNodeType HuffNode[])
{
int m1,m2,x1,x2,len,str_pinlv[52]; //大小写字母共52个
cout<<"请输入待编码的字符串(大/小写英文字母且不含空格):"<<endl;
cin>>str;
len=str.length();
memset(str_pinlv,0,sizeof(str_pinlv));
for(int i=0;i<len;i++)
{//统计字符出新的频率
if(str[i]>='a'&&str[i]<='z')
str_pinlv[str[i]-'a']++;
else if(str[i]>='A'&&str[i]<='Z')
str_pinlv[str[i]-'A'+26]++;
}
n=0;
for(int i=0;i<52;i++)
{//统计出现的字母种类数
if(str_pinlv[i]!=0)
n++;
}
for(int i=0;i<2*n-1;++i)
{//初始化
HuffNode[i].weight=0;
HuffNode[i].parent=-1;
HuffNode[i].lchild=-1;
HuffNode[i].rchild=-1;
}
system("cls");
cout<<endl<<" 字符 频度"<<endl;
for(int i=0,j=0;j<52;++j)
{//初始化节点权值
if(str_pinlv[j]!=0)
{
if(j<26)
{
HuffNode[i].ch=(char)(j+'a');
HuffNode[i++].weight=str_pinlv[j];
cout<<" "<<(char)(j+'a')<<" "<<str_pinlv[j]<<endl;
}
else
{
HuffNode[i].ch=(char)(j+'A'-26);
HuffNode[i++].weight=str_pinlv[j];
cout<<" "<<(char)(j+'A'-26)<<" "<<str_pinlv[j]<<endl;
}
}
}
for(int i=0;i<n-1;++i)
{
m1=m2=MAXVALUE;
x1=x2=0;
for(int j=0;j<n+i;++j)
{
if(HuffNode[j].weight<m1&&HuffNode[j].parent==-1)
{
m2=m1;
x2=x1;
m1=HuffNode[j].weight;
x1=j;
}
else if(HuffNode[j].weight<m2&&HuffNode[j].parent==-1)
{
m2=HuffNode[j].weight;
x2=j;
}
}
HuffNode[x1].parent=n+i;
HuffNode[x2].parent=n+i;
HuffNode[n+i].weight=HuffNode[x1].weight+HuffNode[x2].weight;
HuffNode[n+i].lchild=x1;
HuffNode[n+i].rchild=x2;
//cout<<HuffNode[x1].ch<<"和"<<HuffNode[x2].ch<<"的双亲是:"<<n+i<<endl;
}
cout<<endl<<" 创建哈弗曼树成功! =^_^= "<<endl<<endl;
system("pause");
}
int Send()
{//统计字符频度,建立哈弗曼树,记录编码
string str1="";
HNodeType huffnode[MAXLEAF];
HCodeType HuffCode[MAXLEAF], cd;
HaffmanTree(huffnode);
for (int i=0; i < n; i++)
{
cd.start = n-1;
int c = i;
int p = huffnode[c].parent;
while (p != -1) /* 父结点存在 */
{
if (huffnode[p].lchild == c)
cd.bit[cd.start] = 0;
else
cd.bit[cd.start] = 1;
cd.start--; /* 求编码的低一位 */
c=p;
p=huffnode[c].parent; /* 设置下一循环条件 */
} /* end while */
/* 保存求出的每个叶结点的哈夫曼编码和编码的起始位 */
for (int j=cd.start+1; j<n; j++)
{ HuffCode[i].bit[j] = cd.bit[j];}
HuffCode[i].start = cd.start;
} /* end for */
/* 输出已保存好的所有存在编码的哈夫曼编码 */
system("cls");
cout<<endl<<" 哈弗曼编码如下: "<<endl<<endl;
for (int i=0; i<n; i++)
{
printf ("%c 的哈弗曼编码是: ", huffnode[i].ch);
for (int j=HuffCode[i].start+1; j < n; j++)
{
str1+=(char)(HuffCode[i].bit[j]+'0');
printf ("%d", HuffCode[i].bit[j]);
}
bianma1[str1]=huffnode[i].ch;
bianma2[huffnode[i].ch]=str1;
str1="";
printf ("\n");
}
system("pause");
system("cls");
cout<<endl<<"原来字符串的编码是:"<<endl;
for(int i=0;i<str.length();i++)
cout<<bianma2[str[i]];
cout<<endl;
system("pause");
return 0;
}
void Receive()
{
string str3,str4="";
system("cls");
cout<<"请输入需要破译的代码(只含有0和1且不含空格):"<<endl;
cin>>str3;
for(int i=0;i<str3.length();i++)
{
str4+=str3[i];
if(bianma1[str4]!="")
{
cout<<bianma1[str4];
str4="";
}
}
if(str4!="")
cout<<endl<<"Sorry,译码失败!"<<endl;
cout<<endl;
}
int main()
{
system("color 37");
Send();
Receive();
}
/*
iloveyou
1110010110000110010011
*/