1. 程式人生 > >資料結構——哈夫曼編碼譯碼器

資料結構——哈夫曼編碼譯碼器

題目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;
}