基础数据结构-二叉树-赫夫曼树的解码(详解)

来源:互联网 发布:ps淘宝详情页模板 编辑:程序博客网 时间:2024/05/19 14:38

  本篇是上一篇赫夫曼树构建与编码的后续,稍微详细讲一下解码的算法。

 

Huffman解码算法流程:

1.定义指针p指向赫夫曼树结点,实际是记录结点数组的下标;

2.定义指针i指向编码串,定义ch逐个取编码串的字符;

3.初始化:读入编码串,设置p指向根结点,i为0;

4.执行以下循环:

  a)取编码串的第i个字符放入ch;

  b)如果ch是字符0,表示往左孩子移动,则p跳转到右孩子;

  c)如果ch是字符1,表示往右孩子移动,则p跳转到右孩子;

  d)如果ch非0非1,表示编码串有错误,输出error表示解码失败;

  e)检查p指向的结点是否为叶子;

    i.如果是叶子,输出解码,p跳回根节点;

    ii.如果不是叶子,设置ch为3;

  f)    继续循环,一直到编码串末尾;

5.循环执行完后,如果ch值为3,输出解码失败,否则成功结束。

 

解码关键:赫夫曼编码是一种前缀编码,解码时不会因为编码串混肴;

例如:

字符串AACACBDEDDDD 编码后我们可以获得对应的前缀:

A: 10;B: 1110;C: 110;D: 0;E: 1111

它对应的编码串即为:

 

下面是参考代码,注释我也打得比较详细了,算是边对着代码边讲解吧:

#include <iostream>#include <string>#include <cstring>using namespace std;#define error -1#define ok 1const int MaxW = 9999;    //假设结点权值不超过9999//定义huffman树结点类class HuffNode{    public:    int weight;        //权值    int parent;        //父结点下标    int leftchild;    //左孩子下标    int rightchild;    //右孩子下标    char data;    //解码数据};//定义huffman树类class HuffMan {    private:    void MakeTree();    //建树,私有函数,被公有函数调用    void SelectMin(int pos, int *s1, int *s2);    //从1到pos的位置找出权值最小的两个结点,结点下标存在s1和s2中    public:    int len;        //结点数量    int lnum;    //叶子数量        HuffNode *huffTree;    //huffman树,用数组表示    string * huffCode;        //每个字符对应的huffman编码    void MakeTree(int n,int wt[],char data[]);    //公有函数,被主函数main调用    void Coding();        //公有函数,被主函数main调用    void Destroy();    int Decode(const string codestr,char txtstr[]);};//构建huffman树void HuffMan::MakeTree(int n,int wt[],char data[])        //参数是叶子结点数量和叶子权值{//公有函数,对外接口    int i;    lnum = n;    len = 2*n-1;    huffTree = new HuffNode[2*n];    huffCode = new string [lnum+1];        //位置从1开始计算    //huffCode实质是个二维字符数组,第i行表示第i个字符对应的编码        //huffman树huffTree初始化    for(i=1;i<=len;i++)    {        huffTree[i].weight = wt[i-1]; //第0号不用,从1开始编号        huffTree[i].data=data[i-1];    //解码用    }    for(i=1;i<=len;i++)    {        if(i>n) huffTree[i].weight=0;    //前n个结点是叶子,已经设值        huffTree[i].parent = 0;        huffTree[i].leftchild = 0;        huffTree[i].rightchild=0;    }    MakeTree();    //调用私有函数建树}void HuffMan::SelectMin(int pos,int *s1,int *s2)//找出最小的两个权值的下标//函数采用地址传递的方法,找出的两个下标保存在s1和s2中{    int w1,w2,i;    w1=w2=MaxW;        //初始化w1和w2为最大值,在比较中会被实际的权值替换    *s1=*s2=0;    for(i=1;i<=pos;i++)    //对结点数组进行搜索    {        if(huffTree[i].weight < w1 && huffTree[i].parent == 0)        {    //如果i结点的权值小于w1,且第i结点是未选择的结点(父亲为0)            w2 = w1;        //把w1,s1保存到w2,s2,即原来的第一最小值变成第二最小值(不理解?)            *s2 = *s1;            w1 = huffTree[i].weight;    //把i结点的权值和下标保存到w1,s1,作为第一最小值            *s1=i;        }        else if(huffTree[i].weight < w2 && huffTree[i].parent == 0)        {            w2=huffTree[i].weight;*s2=i;        }        //否则如果i结点的权值小于w2,且i结点是未选中的,把i结点的权值和下标保存到w2和s2,作为第二最小值        }}void HuffMan::MakeTree(){//私有函数,被公有函数调用    int i,s1,s2;    //构造huffman树HuffTree的n-1个非叶子结点    for(i=lnum+1;i<=len;i++)    {        SelectMin(i-1,&s1,&s2);    //找出两个最小权值的下标放入s1和s2中        for(i = lnum+1;i <= len;i++)        {            SelectMin(i-1,&s1,&s2);    //找出两个最小权值的下标放入s1,s1            huffTree[s1].parent = i;        //将找出的两棵权值最小的字数合并为一颗子树            huffTree[s2].parent = i;        //结点s1,s2的父亲设为i            huffTree[i].leftchild = s1;    //i的左右孩子为s1,s2            huffTree[i].rightchild = s2;                huffTree[i].weight = huffTree[s1].weight + huffTree[s2].weight;    //i的权值等于s1,s2权值和        }    }}//销毁huffman树void HuffMan::Destroy(){    len = 0;    lnum = 0;    delete []huffTree;    delete []huffCode;}//huffman编码void HuffMan::Coding(){        char *cd;    int i,c,f,start;        //求n个叶结点的huffman编码    cd = new char[lnum];        //分配求编码的工作空间    cd[lnum-1] = '\0';    for (i=1;i<=lnum;++i)    //逐个字符求huffman编码    {        start = lnum-1;    //编码结束符位置        //书P147第二个for有参考        for(c = i,f = huffTree[i].parent; f != 0; c = f, f = huffTree[f].parent)    //从叶子到根逆向球编码            if(huffTree[f].leftchild == c)            {                 cd[--start] = '0';            }            else            {                cd[--start] = '1';            }            huffCode[i] = new char[lnum-start];    //为第i各字符编码分配空间            huffCode[i].assign(&cd[start]);    //把cd中从start到末尾的编码复制到huffCode中    }    delete []cd;    //释放工作空间}//huffman解码int HuffMan::Decode(const string codestr,char txtstr[])  {    //编码串是codestr,解码结果放在txtstr中    //编码0表示往左孩子移动,编码1表示往右孩子移动     int i,k,c;    char ch;    c = len;    //c表示结点数组的下标,根节点是结点数组的最后一个结点,所以c一开始指向根节点    k = 0;        //tstr的指针      for(i = 0;i < codestr.length();i++)      {        ch = codestr[i];    //取编码串的第i个字符放入变量ch          if(ch == '0')        //如果ch是字符0,向左孩子移动,c跳转到左孩子        {            c = huffTree[c].leftchild;        }        else if(ch == '1')    //同理        {            c = huffTree[c].rightchild;        }        else                //解码失败        {            return error;        }        if(huffTree[c].leftchild == 0 && huffTree[c].rightchild == 0)          {//c指向结点是否叶子,解码串txtstr的第k保存结点c内字符,c跳回根结点            txtstr[k] = huffTree[c].data;              k++;              c = len;          }          else        {            ch ='\0';    //用于检查不完整编码的报错        }    }    if(ch == '\0') return error;    else txtstr[k] = '\0';  //解码成功,加入字符串结束符      return ok;  }  //主函数int main(){    int t,n,i,j,k;    int wt[800];    HuffMan myHuff;    string codestr;    char txtstr[800];    char data[800];    cout<<"请输入解码实例的个数,按回车结束:"<<endl;    cin>> t;    for(i=0;i<t;i++)    {        cout<<"请输入上一步赫夫曼编码第"<<i+1<<"个实例所得权值个数,按回车结束:"<<endl;        cin>>n;        cout<<"请输入上一步赫夫曼编码第"<<i+1<<"个实例所得各个权值(空格间隔),按回车结束后在下一行输入对应的字符,按回车结束:"<<endl;        for(j=0;j<n;j++)            cin>> wt[j];        for(j=0;j<n;j++)            cin>>data[j];        myHuff.MakeTree(n,wt,data);        myHuff.Coding();        cout<<"请输入需要解码的编码串个数,按回车结束:"<<endl;        cin>> k;        for (j=1;j<=k;j++)        {            cout<<"请输入需要解码的第"<<j<<"个编码串,按回车结束:"<<endl;            cin>>codestr;             if(myHuff.Decode(codestr,txtstr) == 1)             {                 cout<<"解码结果:"<<txtstr<<endl;             }             else cout<<"Error!您输入的编码有误,请重新运行本程序并输入正确编码。"<<endl;        }        myHuff.Destroy();    }    return 0;        }

 

以下是我在Huffman编码解码过程中的关键点,总结如下:

1.编码从叶子开始,解码则从根结点开始;

2.数组下标最开始指向赫夫曼树根结点,最终结束于叶子;

3.根据编码表,编码串0为左孩子,1为右孩子,根据左右孩子跳到相应结点,左右孩子为0则解码完成;

4.如果编码串扫描完后停在非叶子结点(左右孩子均为0的中间结点),则解码失败需要报错;

 

作者:Nathaneko

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 vray光源曝光了怎么办 合伙开店要散伙怎么办 白车剐蹭出现小黑点怎么办 小米商城白屏怎么办 戴尔一体机太卡怎么办 当生活陷入困境怎么办 发现邻居偷电怎么办 玩lol延迟过高怎么办 激活码过期了怎么办 天气冷血压高怎么办 买手机刷信用卡怎么办 无限流量降速怎么办 手机流量降速怎么办 阿里巴巴买东西不发货怎么办 小朋友帮帮代写作业怎么办 连凯软件侵权怎么办 中国联通路由器网速慢怎么办 cpu铜触点掉落怎么办 商场没人气商户怎么办 58会员账号冻结怎么办 58账号冻结资金怎么办 超市买过期东西怎么办 淘宝没发货退款怎么办 申请退款发货了怎么办 51微投票异常怎么办 信用卡逾期收到律师函怎么办 淘宝商品被模仿怎么办 电脑数字间距大怎么办 有源码没有模块怎么办 花盆里有爬虫怎么办 家里有长条爬虫怎么办 有了网站源码后怎么办 第五人格闪退怎么办 超市买东西多收钱怎么办 王者荣耀换系统怎么办 夹娃娃夹子松怎么办 天猫魔盒遥控器没了怎么办 合约机被骗了怎么办 二手苹果合约机怎么办 手机系统老化了怎么办 手机显示电池老化怎么办