哈夫曼树编码译码
一、问题描述
-
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
声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文链接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器