資料結構——哈夫曼編碼譯碼器
阿新 • • 發佈:2019-01-09
題目5: 哈夫曼編/譯碼器 [問題描述]
利用哈夫曼編碼進行通訊可以大大提高通道利用率,縮簡訊息傳輸時間,降低傳輸成本。但是,這要求在傳送端通過一個編碼系統對待傳資料預先編碼,在接收端將傳來的資料進行譯碼(復原)。對於雙工通道(即可以雙向傳輸資訊的通道),每端都需要一個完整的編/譯碼系統。試為這樣的資訊收發站寫一個哈夫曼碼的編/譯碼系統。
[基本要求] 一個完整的系統應具有以下功能:
1、I:初始化(Initialization)。從終端讀入字符集大小n,以及n個字元和n個權值,建立哈夫曼樹,並將它存於檔案hfmTree中。
2、 E:編碼(Encoding)。利用以建好的哈夫曼樹(如不在記憶體,則從檔案hfmTree中讀入),
對檔案ToBeTran中的正文進行編碼,然後將結果存入檔案CodeFile中。
3、D:譯碼(Decoding)。利用已建好的哈夫曼樹將檔案CodeFile中的程式碼進行譯碼,結果存入檔案TextFile中。 1、
P:列印程式碼檔案(Print)。將檔案CodeFile以緊湊格式顯示在終端上,每行50個程式碼。
同時將此字元形式的編碼檔案寫入檔案CodePrin中。 2、 T:列印哈夫曼樹(Tree
Printing)。將已在記憶體中的哈夫曼樹以直觀的方式(樹或凹入
表形式)顯示在終端上,同時將此字元形式的哈夫曼樹寫入檔案TreePrint中。
程式碼:
//要編碼的正文儲存在tobetran.txt檔案中,得到的結果存入codefile檔案中 //譯碼的結果存在於textfile.txt檔案中 //二進位制編碼存在於codeprin中 //哈夫曼樹存在於treeprin中 //-abcdefghijklmnopqrstuvwxyz //186 64 13 22 32 103 21 15 47 57 1 5 32 20 57 63 15 1 48 51 80 23 8 18 1 16 1 //this problem is my favourite #include <iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; typedef struct { int weight; //節點權值 int lchild,rchild,parent; //左右孩子和雙親的下標 } HTNode,*HuffmanTree; void menu() { cout<<"================================================="<<endl; cout<<"| ******哈夫曼樹編碼與譯碼****** |"<<endl; cout<<"| 1.建立哈夫曼樹 |"<<endl; cout<<"| 2.生成二進位制碼 |"<<endl; cout<<"| 3.哈夫曼編碼 |"<<endl; cout<<"| 4.哈夫曼譯碼 |"<<endl; cout<<"| 5.顯示程式碼檔案 |"<<endl; cout<<"| 6.列印哈夫曼樹 |"<<endl; cout<<"| 7.退出 |"<<endl; cout<<"================================================="<<endl; } void Select(HuffmanTree HT,int n,int &s1,int &s2) { int i=1; while(HT[i].parent!=0&&i<=n) i++; if(i==n+1) return ; s1=i; i++; while(HT[i].parent!=0&&i<=n) i++; if(i==n+1) return ; s2=i; i++; if(HT[s1].weight>HT[s2].weight) swap(s1,s2); for(; i<=n; i++) { if(HT[i].parent==0) { if(HT[i].weight<HT[s1].weight) s2=s1,s1=i; else if(HT[i].weight<HT[s2].weight) s2=i; } } return ; } void CreatHuffmanTree(HuffmanTree &HT,int n) { if(n<=1) return ; int m=2*n-1; HT=(HuffmanTree)malloc(sizeof(HTNode)*(m+1)); for(int i=1; i<=m; i++) { HT[i].lchild=0; HT[i].parent=0; HT[i].rchild=0; HT[i].weight=0; } for(int i=1; i<=n; i++) cin>>HT[i].weight; int s1,s2; for(int i=n+1; i<=m; i++) { Select(HT,i-1,s1,s2); HT[s1].parent=i; HT[s2].parent=i; HT[i].lchild=s1; HT[i].rchild=s2; HT[i].weight=HT[s1].weight+HT[s2].weight; } return ; } void CreatHuffmanCode(HuffmanTree &HT,char ** &HC,int n) { char *col; HC=(char **)malloc(sizeof(char *)*(n+1)); //分配n個字元編碼的頭指標向量 col=(char *)malloc(sizeof(char)*n); //分配求編碼的工作空間 col[n-1]='\0'; for(int i=1; i<=n; i++) { int str=n-1; int p=i,f=i; while(HT[f].parent!=0) //從葉子到根逆向求編碼 { f=HT[f].parent; if(HT[f].lchild==p) col[--str]='0'; else if(HT[f].rchild==p) col[--str]='1'; p=f; } HC[i]=(char *)malloc(sizeof(char)*(n-str)); //為第i個字元編碼分配空間 strcpy(HC[i],&col[str]); //把編碼串複製到HC中 } free(col); return ; } void TransCode(HuffmanTree HT,char b[],char a[],char c[],int n) { //b陣列是要翻譯的二進位制編碼 //a陣列是葉子對應的字元 //c陣列儲存翻譯得到的內容 FILE *fw; int q=2*n-1; //初始化為根結點的下標 int k=0; int len=strlen(b); if((fw=fopen("textfile.txt","w"))==NULL) cout<<"Open file error!"<<endl; for(int i=0; i<len; i++) { if(b[i]=='0') q=HT[q].lchild; else if(b[i]=='1') q=HT[q].rchild; if(HT[q].lchild==0&&HT[q].rchild==0) //葉子節點,此時可以譯出字元 { c[k++]=a[q]; fputc(a[q],fw); q=2*n-1; } c[k]='\0'; } return ; } void Coding(HuffmanTree &HT,char ** &HC,int n,char a[]) { FILE *fp,*fw; char c; int k; /* char c,*cd; int k,j,jj; HC=(char **)malloc((2*n-1)*sizeof(char *)); cd=(char *)malloc((2*n-1)*sizeof(char)); cd[n-1]='\0'; for(int i=1;i<=n;i++) { int str=n-1; for(j=i,jj=HT[i].parent;j!=0;j=jj,jj=HT[jj].parent) if(HT[jj].lchild==j) cd[--str]='0'; else cd[--str]='1'; HC[i]=(char *)malloc((n-str)*sizeof(char)); strcpy(HC[i],&cd[str]); } free(cd); */ if((fp=fopen("tobetran.txt","r"))==NULL) cout<<"Open file error!"<<endl; if((fw=fopen("codefile.txt","w"))==NULL) cout<<"Open file error!"<<endl; fscanf(fp,"%c",&c); while(!feof(fp)) { for(int i=1; i<=n; i++) if(a[i]==c) { k=i; break; } for(int i=0; HC[k][i]!='\0'; i++) fputc(HC[k][i],fw); fscanf(fp,"%c",&c); } fclose(fp); fclose(fw); return ; } void printf_code() { FILE *fp,*fw; char temp; if((fp=fopen("codefile.txt","r"))==NULL) cout<<"Open file error!"<<endl; if((fw=fopen("codeprin.txt","w"))==NULL) cout<<"Open file error!"<<endl; cout<<"檔案codefile.txt的內容顯示如下:"<<endl; fscanf(fp,"%c",&temp); for(int i=1;!feof(fp);i++) { printf("%c",temp); if(i%50==0) cout<<endl; fputc(temp,fw); fscanf(fp,"%c",&temp); } cout<<endl; cout<<"該編碼檔案已存入檔案codeprin.txt中!"<<endl; fclose(fp); fclose(fw); } void co_tree(unsigned char T[100][100],int s,int *m,int j,HuffmanTree HT) { int k,l; l=++(*m); for(k=0;k<s;k++) T[l][k]=' '; T[l][k]=HT[j].weight; // itoa(HT[j].weight,T[l][k],10); /* int weishu=log10(HT[j].weight); int base=10; while(weishu) { base*=base; weishu--; } while(HT[j].weight) { T[l][k]=HT[j].weight/base; HT[j].weight/=10; base/=10; k++; } */ if(HT[j].lchild) co_tree(T,s+1,m,HT[j].lchild,HT); if(HT[j].rchild) co_tree(T,s+1,m,HT[j].rchild,HT); T[l][++k]='\0'; return ; } void printf_tree(int num,HuffmanTree HT) { unsigned char T[100][100]; FILE *fp; int m=0; co_tree(T,0,&m,2*num-1,HT); if((fp=fopen("treeprin.txt","w"))==NULL) cout<<"Open file error!"<<endl; for(int i=1;i<=2*num-1;i++) { for(int j=0;T[i][j]!='\0';j++) { if(T[i][j]==' ') printf(" "),fputc(T[i][j],fp); else printf("%u",T[i][j]),fprintf(fp,"%u",T[i][j]); } cout<<endl; fputc(10,fp); } cout<<"該哈夫曼樹已儲存到treeprin.txt檔案中!"<<endl; fclose(fp); return ; } int main() { char a[300]; //儲存要編碼的所有字元 char b[300]; //儲存要翻譯的二進位制編碼 char c[300]; //儲存翻譯出來的結果 HuffmanTree HT=NULL; char ** HC; while(1) { char s1[]= {"結點"}; char s2[]= {"字元"}; char s3[]= {"權值"}; char s4[]= {"雙親"}; char s5[]= {"左孩子"}; char s6[]= {"右孩子"}; int flag=1; menu(); int choose; int num; char temp; int cc=0; cout<<"請選擇你要進行的操作(1-4):"; cin>>choose; switch(choose) { case 1: cout<<"請輸入字元的個數:"; cin>>num; cout<<"請依次輸入"<<num<<"個字元:(空格用-來代替)"; for(int i=1; i<=num; i++) cin>>a[i]; cout<<"請依次輸入"<<num<<"個字元的權值:"; CreatHuffmanTree(HT,num); cout<<"建立哈夫曼樹成功,下面輸出該哈夫曼樹的引數。"<<endl; cout<<"結點i"<<"\t字元"<<"\t權值"<<"\t雙親"<<"\t左孩子"<<"\t右孩子"<<endl; for(int i=1; i<=num*2-1; i++) cout<<i<<"\t"<<a[i]<<"\t"<<HT[i].weight<<"\t"<<HT[i].parent<<"\t"<<HT[i].lchild<<"\t"<<HT[i].rchild<<endl; FILE *fp; if((fp=fopen("hfmtree.txt","w"))==NULL) cout<<"Open file hfmtree error!"<<endl; // char ceshi[10]; // scanf("%s",ceshi); // fwrite(&ceshi,sizeof(ceshi),1,fp); fwrite(&s1,sizeof(s1),1,fp); fwrite(&s2,sizeof(s2),1,fp); fwrite(&s3,sizeof(s3),1,fp); fwrite(&s4,sizeof(s4),1,fp); fwrite(&s5,sizeof(s5),1,fp); fwrite(&s6,sizeof(s6),1,fp); fputc(10,fp); for(int i=1; i<=2*num-1; i++) { fprintf(fp,"%-3d ",i); fwrite(&a[i],1,1,fp); fprintf(fp," %-3d ",HT[i].weight); fprintf(fp,"%-3d ",HT[i].parent); fprintf(fp,"%-3d ",HT[i].lchild); fprintf(fp,"%-3d ",HT[i].rchild); fputc(10,fp); } fclose(fp); cout<<"哈夫曼樹已建成並存入檔案中!"<<endl; break; case 2: CreatHuffmanCode(HT,HC,num); cout<<"哈夫曼編碼表已生成,下面輸出哈夫曼編碼表!"<<endl; cout<<"結點i\t"<<"字元\t"<<"權值\t"<<"編碼\t"<<endl; for(int i=1; i<=num; i++) cout<<i<<"\t"<<a[i]<<"\t"<<HT[i].weight<<"\t"<<HC[i]<<endl; break; case 3: cout<<"從檔案tobetran.txt中讀取正文進行編碼"<<endl; Coding(HT,HC,num,a); cout<<"已編碼成功,對應二進位制編碼已存入檔案codefile.txt中!"<<endl; break; case 4: cout<<"從codefile.txt檔案中讀取一串二進位制進行翻譯:"<<endl; if((fp=fopen("codefile.txt","rb"))==NULL) cout<<"Open file error!"<<endl; while(1) { temp = fgetc(fp); //讀一個位元組。 if(temp == EOF) break; //到檔案尾,退出迴圈。 b[cc++] =temp ;//賦值到字元陣列中。 } b[cc]='\0'; cout<<"該二進位制是:"; printf("%s\n",b); fclose(fp); TransCode(HT,b,a,c,num); cout<<"翻譯成功,翻譯結果為:"; printf("%s\n",c); cout<<"該翻譯結果已存入檔案textfile.txt中!"<<endl; break; case 5: printf_code(); break; case 6: cout<<"下面列印哈夫曼樹:"<<endl; printf_tree(num,HT); break; case 7: flag=0; break; default: cout<<"輸入不合法,請重新輸入!"<<endl; continue; } if(flag==0) break; } return 0; }