哈夫曼樹的構建及應用


哈夫曼樹,又稱最優樹,是一類帶權路徑最短的樹。

哈夫曼樹和哈夫曼的存儲表示:

typedef struct{ unsigned int weight; unsigned int parent,lchild,rchild; }HTNode,*HuffmanTree;//動態分配數組存儲哈夫曼樹
typedef char **HuffmanCode;//動態分配數組存儲哈夫曼編碼表

應用:

【問題描述】

利用哈夫曼編碼進行通信可以大大提高信道利用率,縮短信息傳輸時間,降低傳輸成本。但是,這要求在發送端通過一個編碼系統對待傳輸數據預先編碼,在接收端將傳來的數據進行譯碼(復原)。對於雙工信道(即可以雙向傳輸信息的信道),每端都需要一個完整的編/譯碼系統。試為這樣的信息收發站寫一個哈夫曼的編/譯碼系統。

【基本要求】

一個完整的系統應具有以下功能:

(1) I:初始化(Initialization)。從終端讀入字符集大小n,以及n個字符和n個權值,建立哈夫曼樹,並將它存於文件hfmTree中。

(2) E:編碼(Encoding)。利用以建好的哈夫曼樹(如不在內存,則從文件hfmTree中讀入),對文件ToBeTran中的正文進行編碼,然后將結果存入文件CodeFile中。

(3) D:譯碼(Decoding)。利用已經建好的哈夫曼樹將文件CodeFile中的代碼進行譯碼,結果存入文件TextFile中。

(4) P:打印代碼文件(Print)。將文件CodeFile以緊湊格式顯示在終端上,每行50個代碼,同時將此字符形式的編碼寫入文件CodePrint中。

(5) T:打印哈夫曼樹(Tree printing)。將已經在內存中的哈夫曼樹以直觀的方式(樹或凹入表形式)顯示在終端上,同時將此字符形式的哈夫曼樹寫入文件TreePrint中。

【測試數據】

(1) 利用教科書例6-2中的數據調試程序。

(2) 用下表給出的字符集和頻度的實際統計數據建立哈夫曼樹,並實現以下報文的編碼和譯碼:“THIS PROGRAM IS MY FAVORITE”。

字符

       A     B    C    D     E    F    G    H    I    J    K     L    M

頻度

186   64    13    22   32  103    21   15   47   57   1    5     32   20

字符

N     O     P    Q    R     S    T    U    V    W    X    Y     Z

頻度

57    63    15    1    48    51   80   23   8    18   1    16    1

【實現提示】

(1) 編碼結果以文本式存儲在文件CodeFile中。

(2) 用戶界面可以設計為“菜單”方式:顯示上述功能符號,再加上“Q”,表示退出運行Quit。請用戶鍵入一個選擇功能符。此功能執行完畢后再顯示此菜單,直至某次用戶選擇了“Q”為止。

(3) 在程序的一次執行過程中,第一次執行I,D或C命令之后,哈夫曼樹已經在內存了,不必再讀入。每次執行中不一定執行I命令,因為文件hfmTree可能早已建好。

【選作內容】

(1) 上述文件CodeFile中的每個“0”或“1”實際上占用了一個字節的空間,只起到示意或模擬的作用。為最大限度地利用碼點存儲能力,試改寫你的系統,將編碼結果以二進制形式存放在文件CodeFile中。

(2) 修改你的系統,實現對你的系統的源程序的編碼和譯碼(主要是將行尾符編/譯碼問題)。

(3) 實現各個轉換操作的源/目的文件,均由用戶在選擇此操作時指定。

#include<iostream.h>
//using namespace std;
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<fstream.h>
#define num 1000 typedef struct//哈夫曼樹的結構體
{ char ch; int weight;//權值
    int parent,lchild,rchild; }HTNode,*HuffmanTree; typedef char **HuffmanCode; void Select(HuffmanTree &HT,int a,int *p1,int *p2) //Select函數,選出HT樹到a為止,權值最小且parent為0的2個節點
{ int i,j,x,y,count,temp; for(j=1,count=1;j<=a;j++) { if(HT[j].parent==0) { if(count==1) x=j; if(count==2) y=j; count++; } if(count>2) break; } if(HT[x].weight>HT[y].weight)//令x結點權值小於y結點權值
 { temp=y; y=x; x=temp; } i=(x>y?x:y)+1; while(i<=a) { if(HT[i].parent==0) { if(HT[i].weight<HT[x].weight) { y=x; x=i; } else { if(HT[i].weight>=HT[x].weight&&HT[i].weight<HT[y].weight) y=i; } } i++; } *p1=HT[x].weight<=HT[y].weight?x:y; *p2=HT[x].weight>HT[y].weight?x:y; } void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int n)//構建赫夫曼樹HT,並求出n個字符的赫夫曼編碼HC
{ int i,start,c,f,m,w; int p1,p2; char *cd,z; if(n<=1) exit(1); m=2*n-1;//n個葉子結點的哈夫曼樹共有2n-1個結點
    HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//0號單元未使用
    for(i=1;i<=n;i++)//初始化n個葉子結點
 { printf("請輸入第%d字符信息和權值:",i); scanf("%c%d",&z,&w); while(getchar()!='\n') { continue; } HT[i].ch=z; HT[i].weight=w; HT[i].parent=0; HT[i].lchild=0; HT[i].rchild=0; } for(i=n+1;i<=m;i++)//初始化其余結點
 { HT[i].ch='0'; HT[i].weight=0; HT[i].parent=0; HT[i].lchild=0; HT[i].rchild=0; } for(i=n+1;i<=m;i++)//建立哈夫曼樹
 { Select(HT,i-1,&p1,&p2); HT[p1].parent=i;HT[p2].parent=i; HT[i].lchild=p1;HT[i].rchild=p2; HT[i].weight=HT[p1].weight+HT[p2].weight; } //從葉子到根逆向求每個字符的哈夫曼編碼
    HC=(HuffmanCode)malloc((n+1)*sizeof(char *)); cd=(char *)malloc(n*sizeof(char)); cd[n-1]='\0'; for(i=1;i<=n;i++) { start =n-1; for(c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent) { if(HT[f].lchild==c) cd[--start]='0'; else cd[--start]='1'; } HC[i]=(char *)malloc((n-start)*sizeof(char)); strcpy(HC[i],&cd[start]); } free(cd); } int main() { char code[100],h[100],hl[100]; int n,i,j,k,l; ifstream input_file; ofstream output_file; FILE *fp1,*fp2,*fp3,*fp4,*fp5; char choice,str[100]; HuffmanTree HT; HuffmanCode HC; cout<<" 哈夫曼編碼器\n"; while(choice!='Q'&&choice!='q')            //當choice的值不為q且不為Q時循環
 { cout<<"功能: "<<"I(初始化)"<<""<<"E(編碼)"<<""<<"D(譯碼)"<<""<<"P(打印)"<<""<<"Q(退出)\n"; cout<<"請輸入您要選擇的功能:"; cin>>choice; if(choice=='I'||choice=='i')              //初始化赫夫曼樹
 { cout<<"請輸入字符個數:"; cin>>n; HuffmanCoding(HT,HC,n); for(i=1;i<=n;++i) { cout<<HT[i].ch<<":"<<HC[i]<<endl; } fp1=fopen("hfmTree.txt","w"); for(i=1;i<=n;++i) { fprintf(fp1,"(%c %s)\n",HT[i].ch,HC[i]); } fclose(fp1); cout<<"赫夫曼樹已經創建完畢,並且已經放入hfmTree.txt文件中!"<<endl; } else if(choice=='E'||choice=='e')           //進行編碼,並將字符放入ToBeTran.txt,碼值放入CodeFile.txt中
 { printf("請輸入字符:"); gets(str); fp2=fopen("ToBeTran.txt","w"); fprintf(fp2,"%s",str); fclose(fp2); fp3=fopen("CodeFile.txt","w"); for(i=0;i<strlen(str);i++){ for(j=1;j<=n;++j) { if(HT[j].ch==str[i]) { fprintf(fp3,"%s",HC[j]); break; } } } fclose(fp3); cout<<"\n"; fp4=fopen("CodeFile.txt","r"); char H1;//從CodeFile.txt中讀入編碼,輸出在終端
            cout<<"編碼值為:\n"; while(!feof(fp4)) { H1=fgetc(fp4); cout<<H1; } fclose(fp4); cout<<"\n編碼完畢,並且已經存入CodeFile.txt文件!\n"; } else if(choice=='D'||choice=='d')     //讀入CodeFile.txt中的編碼進行譯碼,將譯出來的字符放入Textfile.txt中
 { input_file.open("CodeFile.txt"); if(!input_file){ cout<<"can't open file!"<<endl; return 1; } input_file>>h; input_file.close(); output_file.open("Textfile.txt"); if(!output_file) { cout<<"can't open file!"<<endl; return 1; } k=0; while(h[k]!='\0')           //先用編碼中的前幾個和字符的編碼相比較,然后往后移
 { for(i=1;i<=n;i++){ l=k; for(j=0;j<strlen(HC[i]);j++,l++) { hl[j]=h[l]; } hl[j]='\0'; if(strcmp(HC[i],hl)==0) { output_file<<HT[i].ch; k=k+strlen(HC[i]); break; } } } output_file.close(); input_file.open("Textfile.txt"); if(!input_file){ cout<<"can't open file!"<<endl; return 1; } // input_file>>h;
            input_file.getline(h,100);//獲取文件里的一行
            cout<<"譯碼結果為:"; printf("%s\n",h); input_file.close(); cout<<"譯碼結果已存入Textfile.txt中\n"; } else if(choice=='P'||choice=='p')//每行50個代碼
 { char a[num]; fp4=fopen("CodeFile.txt","r"); fgets(a,num,fp4); cout<<"打印代碼文件:"<<endl; int l=strlen(a); for(int j=0;j<l;j++) { cout<<a[j]; if((j+1)%50==0) cout<<endl; } fclose(fp4); fp5=fopen("CodePrint.txt","w"); for(int k=0;k<l;k++) { fprintf(fp5,"%c",a[k]); if((k+1)%50==0) { fprintf(fp5,"\n"); } } cout<<"\n該字符形式已存入CodePrint.txt中\n"; fclose(fp5); } else if(choice=='Q'||choice=='q')            //退出程序
 { exit(0); } else               //如果選了選項之外的就讓用戶重新選擇
 { cout<<"您沒有輸入正確的步驟,請重新輸入!"<<endl; } cout<<endl; } return 0; }

用上的幾個文本需要自己新建。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM