[问题描述]
利用哈夫曼编码进行通信,可以压缩通信的数据量,提高传输效率,缩短信息的传输时间,还有一定的保密性。现在要求编写一程序模拟传输过程,实现在发送前将要发送的字符信息进行编码,然后进行发送,接收后将传来的数据进行译码,即将信息还原成发送前的字符信息。
[实现提示]
在本例中设置发送者和接受者两个功能,
发送者的功能包括:
①输入待传送的字符信息;
②统计字符信息中出现的字符种类数和各字符出现的次数(频率);
根据字符的种类数和各自出现的次数建立哈夫曼树;
③利用以上哈夫曼树求出各字符的哈夫曼编码;
④将字符信息转换成对应的编码信息进行传送。
接受者的功能包括:
①接收发送者传送来的编码信息;
②利用上述哈夫曼树对编码信息进行翻译,即将编码信息还原成发送前的字符信息。
从以上分析可发现,在本例中的主要算法有三个:
(1)哈夫曼树的建立;
(2)哈夫曼编码的生成;
(3)对编码信息的翻译。
算法思想
先用map来处理处理输入的字符串,统计每个字符出现次数,并将得到的次数赋给HT数组的权值,并且用一个数组记录相应权值的字母用以译码;再利用哈夫曼树编码,其原理是:根据得到的这些权值,构造只有根节点的二叉树,这些二叉树构成一个森林F,在森林F中选取两棵根结点的权值最小的树作为左右子树构造一颗新的二叉树,且重置新的二叉树的根结点的权值为其左右子树上根节点的权值之和,在森林F中删除这两颗二叉树同时将新的到的加入到F中,重复上述步骤直到F中只剩下一棵树为止。在构造哈夫曼树时,首先选择权值小的,这样保证权值大的离根较进,这样一来在计算的树带权路径长度时,会得到最小的带权路径长度,这种算法是一种贪心的算法。
再将得到的每个字符的编码放在同一个数组中,然后传给哈弗译码函数,译码时从根结点开始向下走直到走到叶子结点,然后输出叶子结点的字母,重复这样,直到得到原字符串,然后输出,译码完成。详细见代码。
代码
#include<cstdio>
#include<iostream>
#include<cstdlib>
using namespace std;
#include<cstring>
#include<map>
#include<string>
const int INF=100000000;
typedef char **HuffmanCode;
char *yma;
char zf[100];
char yc[1000];
typedef struct{
int weight;
int parent,lchild,rchild;
}HTNode,*HuffmanTree;
int Init(HuffmanTree &HT){
int k=0,k1=0;
printf("请输入字符串:\n");
map<char,int>m;
map<char,int>::iterator it;
scanf("%s",yc);
int l=strlen(yc);
for(int i=0;i<l;i++)
m[yc[i]]++;
for(it=m.begin();it!=m.end();it++){
HT[++k].weight=it->second;
zf[++k1]=it->first;
}
return k;
}
int Select(HuffmanTree &HT,int i,int n) //选择函数
{
int s,max1=INF;
for(int j=1;j<=n;j++){
if(HT[j].parent==0){//只有当一个节点的双亲是0时才可以参与选择
if(HT[j].weight<=max1){
max1=HT[j].weight;
s=j; //记录那个数的下标
}
}
}
HT[s].parent=i; //将其双亲赋值为i
return s;
}
int CreatTree(HuffmanTree &HT){
int s1,s2,sum=0;
HT=new HTNode[1000];
int k=Init(HT);
for(int j=1;j<=2*k-1;j++){
HT[j].lchild=0;HT[j].rchild=0;HT[j].parent=0;//初始化哈夫曼树
if(j>k)
HT[j].weight=INF+1;
}
for(int j=k+1;j<=(2*k-1);j++){ //开始构建
s1=Select(HT,j,(2*k-1));
s2=Select(HT,j,(2*k-1));
HT[j].lchild=s1;
HT[j].rchild=s2;
HT[j].weight=HT[s1].weight+HT[s2].weight;
}
printf("哈夫曼树创建成功!\n\n");
system("pause");
system("cls");
return k;
}
void CreatHffCode(HuffmanTree HT,HuffmanCode &HC,int n){
HC=new char*[n+1];
yma=new char[n*n];
int k=0;
char *cd;
cd=new char[n];
cd[n-1]='\0';
for(int i=1;i<=n;i++){
int start=n-1;
int c=i,f=HT[i].parent;
while(f!=0){
--start;
if(HT[f].lchild==c)
cd[start]='0';
else
cd[start]='1';
c=f;f=HT[f].parent;
}
HC[i]=new char[n-start];
strcpy(HC[i],&cd[start]);
}
delete cd;
printf("生成哈夫曼编码成功!以下是哈夫曼编码表:\n\n");
printf("节点\t字符\t权值\t编码\n");
for(int i=1;i<=n;i++){
printf("%d\t%c\t%d\t%s\n",i,zf[i],HT[i].weight,HC[i]);
}
printf("\n");
int l=strlen(yc);
for(int i=0;i<l;i++){
for(int j=1;j<=n;j++)
if(yc[i]==zf[j])
strcat(yma,HC[j]);
}
printf("该字符串所生成的编码为:\n");
int ll=strlen(yma);
for(int i=3;i<ll;i++)
printf("%c",yma[i]);
printf("\n\n");
yma[ll]='\0';
system("pause");
system("cls");
}
void TranCode(HuffmanTree HT,int n){
char b[1000];
int t=n*2-1;
int k=0;
for(int i=3;yma[i]!='\0';i++){
if(yma[i]=='0')
t=HT[t].lchild;
else if(yma[i]=='1')
t=HT[t].rchild;
if(HT[t].lchild==0&&HT[t].rchild==0){
b[k++]=zf[t];
t=n*2-1;
}
}
b[k]='\0';
printf("译码成功!该二进制码代表的字符串为:\n");
printf("%s\n\n",b);
system("pause");
system("cls");
}
int main()
{
HuffmanTree HT;
HuffmanCode HC;
int n;
while(1){
printf("---欢迎来到哈夫曼编码\译码系统---\n");
printf("功能:\n");
printf(" 1.创建哈夫曼树.\n");
printf(" 2.哈夫曼树编码.\n");
printf(" 3.哈夫曼译码.\n");
printf(" 4.退出.\n");
printf("---------------------------------\n\n");
printf("请输入您选择的功能编号:\n");
int z;
scanf("%d",&z);
if(z==4){
printf("-----------\n");
printf("谢谢使用!\n");
printf("-----------\n");
break;
}
if(z!=1&&z!=2&&z!=3&&z!=4){
printf("您输入的编号不符合要求,请重新输入!\n");
continue;
}
switch(z){
case 1:n=CreatTree(HT);break;
case 2:CreatHffCode(HT,HC,n);break;
case 3:TranCode(HT,n);break;
}
}
return 0;
}
/*
aadddccccggrs
*/