[问题描述]
利用哈夫曼编码进行通信,可以压缩通信的数据量,提高传输效率,缩短信息的传输时间,还有一定的保密性。现在要求编写一程序模拟传输过程,实现在发送前将要发送的字符信息进行编码,然后进行发送,接收后将传来的数据进行译码,即将信息还原成发送前的字符信息。
[实现提示]
在本例中设置发送者和接受者两个功能,
发送者的功能包括:
①输入待传送的字符信息;
②统计字符信息中出现的字符种类数和各字符出现的次数(频率);
②根据字符的种类数和各自出现的次数建立哈夫曼树;
③利用以上哈夫曼树求出各字符的哈夫曼编码;
④将字符信息转换成对应的编码信息进行传送。
接受者的功能包括:
①接收发送者传送来的编码信息;
②利用上述哈夫曼树对编码信息进行翻译,即将编码信息还原成发送前的字符信息。
从以上分析可发现,在本例中的主要算法有三个:
(1)哈夫曼树的建立;
(2)哈夫曼编码的生成;
(3)对编码信息的翻译。
#include<cstdio>
#include<iostream>
#include<cstring>
#define inf 0x3f3f3f3f
using namespace std;
const int Max_n=26;
const int Max_m=8;
int n=0;
string str;
char strcode[100];//strcode存放编码信息
//结构体构造字符权值(即次数)
typedef struct {
char a;
int weight;
}DataType;
DataType Data[Max_n];
//Huffman的存储表示
typedef struct {
DataType data;//结点信息
int parent,lchild,rchild;
}HTNode,*HuffmanTree;
//输入并统计字符串信息,这里按照出现顺序打印
void PrintStrMess(){
cout<<">>请输入您需要统计的字符串:";
cin>>str;
int a[Max_n];
memset(a,0,sizeof(a));
int len=str.length();
int j=0;
for(int i=0;i<len;i++){
a[str[i]-'a']++;
if(a[str[i]-'a']==1)
Data[j++].a=str[i];
}
for(int i=0;i<j;i++){
Data[i].weight=a[Data[i].a-'a'];
}
cout<<"字符串中出现了"<<j<<"种字符"<<endl;
for(int i=0;i<j;i++){
cout<<Data[i].a<<"出现的次数为:"<<Data[i].weight<<endl;
}
n=j;
cout<<endl;
cout<<">>任意键返回主菜单."<<endl;
system("pause");
system("cls");
}
/*找到最小的两个,传引用*/
void Select(HuffmanTree &HT,int n,int &mmin1,int &mmin2){
int start;//记录遍历起点
for(int i=1;i<=n;i++){
if(HT[i].parent==0){
start=i;
break;
}
}
HT[mmin1].data.weight=inf;
for(int i=start;i<=n;i++){
if(HT[i].data.weight<=HT[mmin1].data.weight&&HT[i].parent==0)
mmin1=i;//遍历,如果权值比当前值小,就更新
}
for(int i=start;i<=n;i++){
if(HT[i].data.weight<=HT[mmin2].data.weight&&HT[i].parent==0&&i!=mmin1){
mmin2=i;
}
}
}
/*创建Huffman树*/
void CreatHuffmanTree(HuffmanTree &HT,int n){
if(n<=1) return ;
HT=new HTNode[2*n];//0号单元未使用,所以需要动态分配m+1个单元,HT[m]表示根结点
//初始化结点
for(int i=1;i<=2*n-1;i++){
HT[i].parent=0;HT[i].lchild=0;HT[i].rchild=0;
HT[i].data.a=Data[i-1].a;HT[i].data.weight=Data[i-1].weight;//Data从0开始存入信息
}
for(int i=n+1;i<=2*n-1;i++){
int mmin1=0,mmin2=0;//下标
Select(HT,i-1,mmin1,mmin2);
HT[mmin1].parent=i;HT[mmin2].parent=i;//找到的两个值标记双亲为i,表示这两个值已经处理过
HT[i].lchild=mmin1;HT[i].rchild=mmin2;//mmin1,mmin2分别为双亲的左右孩子
HT[i].data.weight=HT[mmin1].data.weight+HT[mmin2].data.weight;//双亲权值是左右孩子权值之和
}
}
typedef char **HuffmanCode;//动态分配数组存储哈夫曼编码表
/*哈夫曼编码*/
void CreatHuffmanCode(HuffmanTree &HT,HuffmanCode &HC,int n){
//从叶子到根逆向求每个字符的哈夫曼编码,存储在编码表HC中
HC=new char*[n+1];//为HC分配n个字符编码的空间,0号位置不用
char *cd=new char[n];
cd[n-1]='\0';//编码结束符
for(int i=1;i<=n;i++){//组个字符求编码
int start=n-1;//记录编码在cd中存放的位置,初始时指向最后
int c=i;int f=HT[i].parent;//c记录从叶子结点向上回溯至根节点的下标,f记录i的双亲的下标
while(f!=0){//知道向上没有双亲
start--;//向上走一次,start前移一次
if(HT[f].lchild==c) cd[start]='0'; //左孩子编码即为0
else cd[start]='1'; //右孩子编码即为1
c=f;f=HT[f].parent; //继续向上找
}
HC[i]=new char[n-start];//第i个编码字符分配空间
strcpy(HC[i],&cd[start]);
}
delete cd;
}
/*创建Huffman树,求哈夫曼编码*/
void Creat(HuffmanTree &HT,HuffmanCode &HC){
CreatHuffmanTree(HT,n);
cout<<"哈夫曼树创建成功!"<<endl;
CreatHuffmanCode(HT,HC,n);
cout<<"成功求出哈夫曼编码,任意键继续后续操作!"<<endl;
system("pause");
system("cls");
}
/*打印输入的字符串对应的编码*/
void PrintStrCode(HuffmanTree &HT,HuffmanCode &HC){
for(int i=1;i<=n;i++){
cout<<HT[i].data.a<<"所对应的哈夫编码为:"<<HC[i]<<endl;
}
cout<<endl;
cout<<"----------------------------------------------------------"<<endl;
cout<<endl;
strcode[0]='\0';
cout<<str<<"对应的编码为(即为发送者发送编码):";
int len=str.length();
for(int i=0;i<len;i++){
for(int j=1;j<=n;j++){
if(str[i]==HT[j].data.a){
cout<<HC[j];
strcat(strcode,HC[j]);
break;
}
}
}
cout<<endl;
cout<<endl;
cout<<">>按任意键进入主界面选择解码操作:"<<endl;
system("pause");
system("cls");
}
/*译码*/
void TracStrCode(HuffmanTree &HT){
cout<<"接收到的信息为:"<<strcode<<endl;
int root=2*n-1;//根结点
int i=0;//向下搜索的方向(0为左,1为右)
cout<<"接收到的信息对应的字符为:";
while(strcode[i]!='\0'){
if(strcode[i]=='0')
root=HT[root].lchild;
else
root=HT[root].rchild;
if(!HT[root].lchild&&!HT[root].rchild){
printf("%c",HT[root].data.a);
root=2*n-1;
}
i++;
}
cout<<endl;
cout<<"(接收者正确字符串应为:"<<str<<")"<<endl;
cout<<endl;
cout<<"恭喜你完成一次发送接收信息的操作"<<endl;
cout<<">>按任意键进入主界面进行新的操作"<<endl;
system("pause");
system("cls");
}
void Menu(HuffmanTree &HT,HuffmanCode &HC){
cout<<"----------------------------------------------------------"<<endl;
cout<<" 1.输入字符串统计字符串信息(类数,各字符出现的频数)"<<endl;
cout<<" 2.建立哈夫曼树并且求出哈夫曼编码"<<endl;
cout<<" 3.打印各字符的哈弗编码及发送的编码信息"<<endl;
cout<<" 4.接收的编码信息及编码结果(打印字符)"<<endl;
cout<<" other(其他输入).退出"<<endl;
cout<<"----------------------------------------------------------"<<endl;
//(输入,统计,发送,接收是完整的一次)
cout<<endl<<">>请您按照顺序依次选择功能:";
int chose;
cin>>chose;
switch(chose){
case 1: {
PrintStrMess();
Menu(HT,HC);
}
case 2: {
Creat(HT,HC);
Menu(HT,HC);
}
case 3: {
PrintStrCode(HT,HC);
Menu(HT,HC);
}
case 4: {
TracStrCode(HT);
Menu(HT,HC);
}
default : exit(0);
}
}
int main(){
HuffmanTree HT;
HuffmanCode HC;
Menu(HT,HC);
return 0;
}