C++——赫夫曼编码-译码器(Huffman Coding)

26 篇文章 1 订阅

基本概念

哈夫曼编码(Huffman Coding):又称霍夫曼编码、赫夫曼编码-,是一种编码方式,哈夫曼编码是可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,一般就叫做Huffman编码(有时也称为霍夫曼编码)。

源代码 

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
 
using namespace std;
 
typedef struct
{
    char letter, *code;
    int weight;
    int parent, lchild, rchild;
} HTNode, *HuffmanTree;
//char a[28]=" ABCDEFGHIJKLMNOPQRSTUVWXYZ";//存字符
char a[100];
int b[100];//存权值
int n=0;//字符个数
char code[100];//存输入的二进制代码。
void CreateHuffmanTree(HuffmanTree &HT, char t[], int w[])
{
    int m, s1, s2;
    m=2*n-1; //总共需要2n-1个节点
    HT=new HTNode[m+1];//开辟空间
    for(int i=0; i<n; i++)//给叶子结点赋值。
    {
        HT[i].letter=t[i];
        HT[i].weight=w[i];
    }
    for(int i=0; i<=m; i++)//初始化所有结点的父节点,左孩子、右孩子都为0.
    {
        HT[i].code='\0';
        HT[i].parent=HT[i].lchild=HT[i].rchild=-1;
    }
    for(int i=n; i<m; i++)//处理每个非叶子结点
    {
        int min1,min2;
        min1=min2=9999;
        s1=s2=-1;
        for(int k=0; k<=i-1; k++) //找到两个权值最小的结点作为左右子树的根节点构造新的二叉树。
        {
            if(HT[k].parent==-1)//只在尚未构造成二叉树的结点中查找。
            {
                if(HT[k].weight<min1)
                {
                    min2=min1;
                    s2=s1;
                    min1=HT[k].weight;
                    s1=k;
                }
                else if(HT[k].weight<min2)
                {
                    min2=HT[k].weight;
                    s2=k;
                }
            }
        }
        HT[s1].parent=i;HT[s2].parent=i;//将他们两个的父节点设置为 i;
        HT[i].lchild=s1;HT[i].rchild=s2;//把这两个分别当作 结点i 的左右孩子。
        HT[i].weight=HT[s1].weight+HT[s2].weight;//他们两个的双亲为他们两个的和。
        HT[i].letter='#';
    }
}
void CreatHuffmanCode(HuffmanTree HT)//编码。
{
    FILE *fp;
    int start, c, f;
    int i;
    char *cd=new char [n];
 
    cd[n-1]='\0';
    if((fp=fopen("OutMa.txt","w"))==NULL)  //输出哈夫曼编码到文件
    {
        printf("打开输出文件失败。\n");
        exit(0);
    }
    fprintf(fp,"每个字符对应的哈夫曼编码:\n");
    cout<<endl<<"每个字符对应的哈夫曼编码为:"<<endl;
    for(i=0; i<n; i++)
    {
 
        start=n-1;
        c=i;
        f=HT[i].parent;
 
        while(f!=-1)
        {
            start--;
            if(HT[f].lchild==c)
            {
 
                cd[start]='0';
            }
            else
            {
 
                cd[start]='1';
            }
            c=f;
            f=HT[f].parent;
        }
 
        HT[i].code=new char[n-start];
        strcpy(HT[i].code,&cd[start]);
        cout<<HT[i].letter<<": "<<HT[i].code<<endl;
        fprintf(fp,"%c  %s\n",HT[i].letter,HT[i].code);
    }
    fprintf(fp,"编码完成!\n");
    fclose(fp);
    delete cd;
}
 
void ReadData()//从文件中读取权值
{
    FILE *fp1;
    if(NULL== (fp1=fopen("data.txt","r")))
    {
        cout<<"error"<<endl;
        exit(1);
    }
        int i=0;
        while(fscanf(fp1,"%c%d",&a[i],&b[i])!=EOF)
            n++,i++;
            n--;
//        memset(b,0,sizeof(b));
//        char k;
//        n=27;
//        while(fscanf(fp1,"%c",&k)!=EOF)
//        {
//            for(int i=0;i<27;i++)
//            {
//                if(k==a[i])
//                b[i]++;
//            }
//        }
    fclose(fp1);
    cout<<"各个数据及其对应权值为:"<<endl;
    for(int i=0; i<n; i++)
    {
        cout<<a[i]<<":"<<b[i]<<"      ";
        if(i&&(i+1)%3==0)
            cout<<endl<<endl;
    }
}
 
void Yima(HuffmanTree HT,char cod[],int b)           //译码
{
    FILE *fp;
    if((fp=fopen("Translate.txt","w"))==NULL)
    {
        cout<<"打开翻译文件失败。"<<endl;
        exit(0);
    }
    char sen[100];
    char temp[50];
    char blank[]=" ";       //空白字符串
    int t=0;
    int s=0;
    int xx=0;
    for(int i=0; i<b; i++)
    {
        temp[t++]=cod[i];     //读取字符
        temp[t] = '\0';
        for(int j=0; j<n; j++)       //依次与所有字符编码开始匹配
        {
 
            if (!strcmp(HT[j].code,temp))                 //匹配成功
            {
 
                sen[s]=HT[j].letter;    //将字符保存到sen中
                s++;
                xx+=t;//用于寻找出错的位数,如果有某一位没有匹配则记录下来。
                strcpy(temp,blank);
                t=0;
                break;
            }
        }
    }
    if(t==0)      //t如果被置空了,表示都匹配出来了,打印译码
    {
 
        sen[s]='\0';
        cout<<"译码为:"<<endl<<sen<<endl;
        fprintf(fp,"%s",sen);
        fclose(fp);
    }
    else                              //t如果没有被置空 , 源码无法被完全匹配
    {
        cout<<"二进制源码有错,不存在此编码,从第"<<xx+1<<"位开始"<<endl;
    }
}
void InCode(HuffmanTree HT)
{
    cout<<"译码:"<<endl;
    int x,k,symbol;
    char p;
    while(1)
    {
        cout<<"请输入要译码的二进制字符串,输入'#'结束:"<<endl;
        x=1;//判断是否有非法字符只能是0 1
        k=0;//作为循环变量来使code【k】=输入的字符
        symbol=1;//判断是否输入结束
        while(symbol)//输入二进制代码。
        {
            cin>>p;
            if(p!='1'&&p!='0'&&p!='#')  //若存在其它字符,x设为0,表示输入的不是二进制数。
            {
                x=0;
            }
            code[k]=p;
            k++;
            if(p=='#')
                symbol=0;  //#号结束标志
        }
        if(x==1)
            Yima(HT,code,k-1);        //进行译码
        else
        {
            cout<<"有非法字符!"<<endl;
        }
        cout<<"输入Y继续进行编码,其他任意键退出译码:"<<endl;
        cin>>p;
        if(p=='y'||p=='Y')
            continue;
        else
            break;
    }
}
void PrintHF1(HuffmanTree HT,int k,string ss)
{
        ss+="    ";
        if(HT[k].lchild==-1||HT[k].rchild==-1)
            {
                cout<<ss;
                cout<<HT[k].letter<<endl;
                return;
            }
            PrintHF1(HT,HT[k].rchild,ss);
 
            cout<<ss;
            cout<<HT[k].weight<<endl;
            PrintHF1(HT,HT[k].lchild,ss);
}
void PrintHF2(HuffmanTree HT,int k)
{
    if(HT)
    {
        if(HT[k].letter=='#')
            cout<<HT[k].weight;
        else
            cout<<HT[k].letter;
        if(HT[k].lchild!=-1||HT[k].rchild!=-1)
        {
            cout<<"(";
            PrintHF2(HT,HT[k].lchild);
            cout<<",";
            PrintHF2(HT,HT[k].rchild);
            cout<<")";
        }
    }
}
 
void menu()
{
    cout<<endl<<endl<<endl<<endl; 
	cout<<"                                          哈夫曼编码/译码器                     "<<endl;
    cout<<"                    ************************************************************"<<endl;
    cout<<"                    *                                                          *"<<endl;
    cout<<"                    *             请输入以下数字选择功能                       *"<<endl;
    cout<<"                    *                                                          *"<<endl;
    cout<<"                    *   1:从文件中读取数据                                     *"<<endl;
    cout<<"                    *   2:建立哈夫曼树                                         *"<<endl;
    cout<<"                    *   3:显示哈夫曼树                                         *"<<endl;
    cout<<"                    *   4:对哈夫曼树进行编码                                   *"<<endl;
    cout<<"                    *   5:对输入代码进行译码                                   *"<<endl;
    cout<<"                    *   输入其他字符退出系统                                   *"<<endl;
    cout<<"                    *                                                          *"<<endl;
    cout<<"                    ************************************************************"<<endl<<"                    请输入数字:";
}
 
int main()
{
    char m;
    HuffmanTree HT;
    int p=2*n-1;
    while(m!=5)
    {
        menu();
		cin>>m;
        if(m=='1')
        {
            system("cls");
            ReadData();//从文件中读取数据。
            cout<<"读入数据成功!"<<endl<<"继续输入数字选择功能:"<<endl;
        }
        else if(m=='2')
        {
            system("cls");
            CreateHuffmanTree(HT, a, b);//建立哈夫曼树。
            cout<<"建立哈夫曼树成功!"<<endl<<"继续输入数字选择功能:"<<endl;
        }
        else if(m=='4')
        {
            system("cls");
            CreatHuffmanCode(HT);//对哈夫曼树进行编码。
            cout<<"哈夫曼编码成功!可打开OutMa.txt查看。"<<endl<<"继续输入数字选择功能:"<<endl;
        }
        else if(m=='5')
        {
            system("cls");
            InCode(HT);//对输入代码进行译码。
            cout<<"译码结果同时存入Translate.txt文件,可打开查看"<<endl<<"输入6退出系统。"<<endl;
        }

        else if(m=='3')
        {
            system("cls");
            int i=0;
            while(i>=0)//找到根节点
            {

                if(HT[i].parent==-1)
                    break;
                else
                {
                    i++;
                    continue;
                }

            }
                cout<<"凹入法显示哈夫曼树:"<<endl;
                string ss="";
                PrintHF1(HT,i,ss);
                cout<<"括号法显示哈夫曼树:"<<endl;
                PrintHF2(HT,i);
            cout<<endl<<"继续输入数字选择功能:"<<endl;
        }
        else
            break;
    }
    return 0;
}

测试数据

data.txt

 186A64B13C22D32E103F21G15H47I57J1K5L32M20N57O63P15Q1R48S51T80U23V8W18X1Y16Z1

参考文章

https://blog.csdn.net/Wood_Du/article/details/80366094

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Starzkg

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值