哈夫曼樹編碼譯碼


一:問題描述

【問題描述】
利用哈夫曼編碼進行通信可以大大提高信道利用率,縮短信息傳輸時間,降低傳輸成本。但是,這要求在發送端通過一個編碼系統對待傳數據預先編碼,在接收端將傳來的數據進行譯碼(復原)。對於雙工信道(即可以雙向傳輸信息的信道),每端都需要一個完整的編/譯碼系統。試為這樣的信息收發站寫一個哈夫曼碼的編/譯碼系統。
【任務要求】
一個完整的系統應具有以下功能:
1) I:初始化(Initialization)。從終端讀入字符集大小n,以及n個字符和n個權值,建立哈夫曼樹,並將它存於文件hfmTree中。
2) E:編碼(Encoding)。利用已建好的哈夫曼樹(如不在內存,則從文件hfmTree中讀入),對文件ToBeTran中的正文進行編碼,然后將結果存入文件CodeFile中。
3) D:譯碼(Decoding)。利用已建好的哈夫曼樹將文件CodeFile中的代碼進行譯碼,結果存入文件TextFile中。

【測試數據】
用下表給出的字符集和頻度的實際統計數據建立哈夫曼樹,並實現以下報文的編碼和譯碼:“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.構建哈夫曼樹

有n個字母待建樹,申請p[2n-1]個結構體數組空間,將各個字母及其權值存入結構體數組,將數組按照權值進行排序,每次選取最小的兩個構建哈夫曼樹,每次從上次選擇的下一個開始選擇,即第一次p[0],p[1]則第二次p[2],p[3],以此類推,每次生成的從p[n+1]開始存入,直至數組就剩下一個元素,即為根節點。

2.編碼

從葉子結點(帶權字母即葉子結點,從1步驟的結構體數組那里入手,需要判斷一下是否為葉子結點)開始向上回溯即尋找父節點,若為父節點左子樹編碼為‘0’字符,反之為‘1’字符,建個數組從最后一位往前存入直至父節點為空即根節點,直至結構體數組最后一個元素。然后將存儲的字母的編碼和待編碼的字符串進行比對,將字符串譯碼為01結構,編碼完成。

3.譯碼

將01結構的編碼從根節點開始譯碼,0往左子樹遍歷1往右子樹,直至葉節點,譯碼出一個字母,然后再從根節點開始,循環操作直至所有都譯碼完成。

三:具體實現

#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();
}

四:運行結果

 具體編譯碼結果可以查看生成的編碼文件以及譯碼文件

五:總結

1.字母以字符形式寫入文件時會亂碼,不能用%c,可以改為%s整體寫入,注意字符數組末尾要加‘\0’轉為字符串。

2.將指針傳入函數,想改變指針的值即傳入的指針再調用完后改變,即如test(p)(main 函數里的),void test(int **p)(main函數外的),需要設二級指針,即指向指針的指針,指針也是變量,和普通變量一樣,只不過是地址類型,而且可以訪問指針所存地址里的東西。

3.結構體數組即每個元素都是結構體,指向結構體的二級指針即二級指針元素是一級指針,一級指針元素是結構體類型數據。

 

 

 


免責聲明!

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



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