哈夫曼树编码译码
一、问题描述
-
1、问题描述
-
利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发站写一个哈夫曼码的编/译码系统。
-
2、任务要求
-
A、初始化(Initialization)。从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmTree中。
B、编码(Encoding)。利用已建好的哈夫曼树(如不在内存,则从文件hfmTree中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。
C、译码(Decoding)。利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件TextFile中。
-
3、测试数据
-
A、用下表给出的字符集和频度的实际统计数据建立哈夫曼树。
B、并实现以下报文的编码和译码:“THIS PROGRAM IS MY FAVORITE”。
二、大致思路
-
1、构建哈夫曼树
-
有n个字母待建树,申请p[2n-1]个结构体数组空间,将各个字母及其权值存入结构体数组,将数组按照权值进行排序,每次选取最小的两个构建哈夫曼树,每次从上次选择的下一个开始选择,即第一次p[0],p[1]则第二次p[2],p[3],以此类推,每次生成的从p[n+1]开始存入,直至数组就剩下一个元素,即为根节点。
-
2、编码
-
带权字母即叶子结点,从1步骤的结构体数组那里入手,需要判断一下是否为叶子结点)开始向上回溯即寻找父节点,若为父节点左子树编码为“0”字符,反之为“1”字符,建个数组从最后一位往前存入直至父节点为空即根节点,直至结构体数组最后一个元素。然后将存储的字母的编码和待编码的字符串进行比对,将字符串译码为01结构,编码完成。
-
3、译码
-
将01结构的编码从根节点开始译码,0往左子树遍历1往右子树,直至叶节点,译码出一个字母,然后再从根节点开始,循环操作直至所有都译码完成。
三、代码实现及结果
-
1、具体编译码结果可以查看生成的编码文件以及译码文件。
-
2、实现(仅供参考)
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef struct node
{
char ch[2];
int weight;
char ldata,rdata;
node *lchild,*rchild,*parent;
}*huffman;
typedef struct encoding
{
char ch[2];
char *data;
}*encode1;
void paixu(huffman *p,int m)
{
huffman temp;
for(int i=0;i<m-1;i++)
{
for(int j=i+1;j<m;j++)
{
if(p[i]->weight>p[j]->weight)
{
temp=p[i];
p[i]=p[j];
p[j]=temp;
}
}
}
}
huffman* Read(int n) //读取英文字母
{
huffman *huff;
FILE *fp;
huff=new huffman[2*n-1];
for(int i=0;i<2*n-1;i++)
{
huff[i]=new node;
huff[i]->lchild=huff[i]->rchild=NULL;
}
if((fp=fopen("hfmTree.txt","w"))==NULL)
{cout<<"文件打开失败!";exit(0);}
cout<<"please input the letter:"<<endl;
for(int i=0;i<n;i++)
{
huff[i]->ch[0]=cin.get();
if(huff[i]->ch[0]=='\n')
huff[i]->ch[0]=cin.get();
cin>>huff[i]->weight;
cin.get();
huff[i]->ch[1]='\0';
fprintf(fp,"%s%d ",huff[i]->ch,huff[i]->weight);
}
cout<<"hfmTree.txt文件写入完成!"<<endl;
fclose(fp);
return huff;
}
node* Crhuffman(huffman* p,int n,int *n1) //创建哈夫曼树
{
int m=n;
for(int i=0;i<2*n-1;i+=2)
{
if((m-1)!=i)
{
paixu(p,m);
p[m]->weight=p[i]->weight+p[i+1]->weight;
p[m]->lchild=p[i];
p[m]->rchild=p[i+1];
p[i]->parent=p[m];
p[i+1]->parent=p[m];
m+=1;
}
else
{p[m-1]->parent=NULL;break;}
}
*n1=m;
return p[m-1];
}
int Code(huffman *p,int n,int t,char *c,int c1,char *c2) //哈弗曼编码
{
huffman q;
FILE *fp;
q=new node;
int k=0,k1=t-1,kk=0;
encode1 *encode;
encode=new encode1[t];
for(int i=0;i<t;i++)
{
encode[i]=new encoding;
encode[i]->data=new char[t];
}
for(int i=0;i<t;i++)
{
for(int j=0;j<t;j++)
{
encode[i]->data[j]='a';
}
}
for(int i=0;i<n;i++)
{
if(p[i]->lchild==NULL&&p[i]->rchild==NULL)
{
q=p[i];
encode[k]->ch[0]=p[i]->ch[0];
while(q->parent!=NULL)
{
if(q->parent->lchild==q)
encode[k]->data[k1]='0';
else
encode[k]->data[k1]='1';
q=q->parent;
k1=k1-1;
}
k+=1;
k1=t-1;
}
}
if((fp=fopen("CodeFile.txt","w"))==NULL)
{cout<<"文件打开失败!"<<endl;exit(0);}
for(int i=0;i<t;i++)
{
encode[i]->ch[1]='\0';
fprintf(fp,"%s:",encode[i]->ch);
for(int j=0;j<t;j++)
{
if(encode[i]->data[j]!='a')
{
fprintf(fp,"%c",encode[i]->data[j]);
if(j==t-1)
fprintf(fp,"\n");
}
}
}
for(int i=0;i<c1;i++)//编码
{
for(int j=0;j<t;j++)
{
if(c[i]==encode[j]->ch[0])
for(int k2=0;k2<t;k2++)
{
if(encode[j]->data[k2]!='a')
{
c2[kk]=encode[j]->data[k2];
kk+=1;
}
}
}
}
c2[kk]='\0';
fprintf(fp,"#题目所给英文编码: %s",c2);
cout<<"CodeFile文件写入完成!"<<endl;
fclose(fp);
return kk;
}
int Readtxt(char *c) //读取英文字符串
{
int m=0;
FILE *fp;
cout<<"请输入需要编码的大写英文:"<<endl;
if((fp=fopen("ToBeTran.txt","w"))==NULL)
{cout<<"文件打开失败!";exit(0);}
for(int i=0;;i++)
{
c[i]=cin.get();
if(c[i]=='\n')
{c[i]='\0';break;}
m+=1;
}
fprintf(fp,"%s",c);
fclose(fp);
cout<<"ToBeTran.txt文件写入完成!"<<endl;
return m;
}
void Initialization(huffman **p,node **p1,int &n,int &n1) //初始化
{
cout<<"请输入待插入英文字母个数:"<<endl;
cin>>n;
*p=Read(n);
*p1=Crhuffman(*p,n,&n1);
}
void Encoding(int &c1,char **c,char **c2,int n,int n1,huffman *p) //编码
{
c1=Readtxt(*c);
Code(p,n1,n,*c,c1,*c2);
}
void Decoding(node *p1,char *c) //译码
{
int k=0,i=0;
FILE *fp;
node *p=p1;
if((fp=fopen("TextFile.txt","w"))==NULL)
{cout<<"文件打开失败!";exit(0);}
while(c[i]!='\0')
{
while(c[i]!='\0')
{
if(c[i]=='0')
{
p=p->lchild;
if(p->lchild==NULL&&p->rchild==NULL)
{i++;break;}
}
else
{
p=p->rchild;
if(p->lchild==NULL&&p->rchild==NULL)
{i++;break;}
}
i++;
}
fprintf(fp,"%s",p->ch);p=p1;
}
fclose(fp);cout<<"TextFile.txt文件写入完成!"<<endl;
}
void Print() //打印
{
FILE *fp,*fp1;
char ch[1000];
if((fp=fopen("CodeFile.txt","r"))==NULL)
{cout<<"文件打开失败!"<<endl;exit(0);}
if((fp1=fopen("CodePrint.txt","w"))==NULL)
{cout<<"文件打开失败!"<<endl;exit(0);}
while(!feof(fp))
{
fscanf(fp,"%s",ch);
if(ch[0]=='#')
{
cout<<ch<<endl;
fscanf(fp,"%s",ch);
fprintf(fp1,"%s",ch);
}
cout<<ch<<endl;
}
fclose(fp);
fclose(fp1);
}
void Tree_printing(node *p1)
{
FILE *fp;
if((fp=fopen("TreePrint.txt","a"))==NULL)
{cout<<"文件打开失败!"<<endl;exit(0);}
if(p1)
{
if(p1->ch[0]==NULL)
{
cout<<"空"<<p1->weight<<" ";
fprintf(fp,"空%d\n",p1->weight);
}
else
{
cout<<p1->ch[0]<<p1->weight<<" ";
fprintf(fp,"%c%d\n",p1->ch[0],p1->weight);
}
Tree_printing(p1->lchild);
Tree_printing(p1->rchild);
}
}
void homepage()
{
cout<<"****************************************************************************"<<endl;
cout<<" 1.Initialization()&&Encoding() //初始化并编码 "<<endl;
cout<<" 2.Decoding() //译码 "<<endl;
cout<<" 3.Print() //打印代码文件 "<<endl;
cout<<" 4.Tree_printing() //打印哈夫曼树 "<<endl;
cout<<" 5.Confirm exit //确认退出 "<<endl;
cout<<"****************************************************************************"<<endl;
cout<<"请选择需要的操作:"<<endl;
}
void display()
{
int N;huffman *p,i=0;
node *p1;
encode1 *q;
char *c,*c2;
c=new char[100];
c2=new char[1000];
int n,n1,c1,n2;
homepage();
cin>>N;
while(1)
{
// system("cls");
if(N==1)
{
Initialization(&p,&p1,n,n1);
Encoding(c1,&c,&c2,n,n1,p);
i+=1;
}
else if(N==2)
{
if(i==0)
{cout<<"操作不合法,请重新输入!"<<endl;}
else
{Decoding(p1,c2);i+=1;}
}
else if(N==3)
{
if(i==0)
{cout<<"操作不合法,请重新输入!"<<endl;}
else
{cout<<" ";Print();i+=1;}
}
else if(N==4)
{
if(i==0)
{cout<<"操作不合法,请重新输入!"<<endl;}
else
{Tree_printing(p1);i+=1;cout<<endl;}
}
else if(N==5)
exit(0);
else
printf("输入不合法,请重新输入!\n");
homepage();
cin>>N;
}
}
int main()
{
display();
}
-
3、运行示例
四、小结
-
1、可以以%s形式整体写入,注意字符数组末尾要加 `\0` 转为字符串。
-
2、结构体数组每个元素都是结构体,指向结构体的二级指针即二级指针存放的是一级指针地址,一级指针存放的是结构体类型数据的地址。
作者:知微smile
出处:https://www.cnblogs.com/zwsmile/p/14150552.html
声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文链接,否则保留追究法律责任的权利。
出处:https://www.cnblogs.com/zwsmile/p/14150552.html
声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文链接,否则保留追究法律责任的权利。