记信息论与编码之课设-哈夫曼编码

 哈夫曼编码(Huffman Coding),又称霍夫曼编码,是一种编码方式, 

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

   首先,将符号按照概率由大到小排队编码时,从最小概率的两个符号开始,可选其中一个支 路为0,另一支路为1。这里,我们选上支路为0,下支路为1。再将已编码的两支路的概率合并,并重新排队。多次重复使用上述方法直至合并概率归一时为止   

 从文件中或者输入 读入一个字符串,统计字符串中的字符种类(不包括大小写),以及每个字符出现的概率,并按照霍夫曼编程思想,构造哈夫曼树结构,将其转化成01”的二进制字符串,并且可以读入一串01字符串,将其译为对应的哈夫曼字符。并计算其编码效率。

基本算法:

①  输入字符串 (不包含大小写),回车结束

     ②  根据循环遍历统计字符串中每个不同字符的概率以及总个数

     ③  根据哈夫曼编码构造哈夫曼树

         先将数组排序,采用优先队列求出最小的两个概率,求和得到父节点 ,加入哈夫曼树数组中,依次循环直到只剩下一个概率且为1的根节点

      ④  编码,从每个叶子节点向根遍历,如果是左子树编码就为0,右子树为1.直到所有的叶子节点都编码完毕。

      ⑤   根据公式求编码效率n == H(s)/l

     ⑥  译码 ,根据输入的字符串,从根节点开始想下边遍历,如果是1就向右子树,0就是左子树,直到找到一个叶子节点就退出。继续从根节点开始。

代码如下:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<queue>
#include<stack>
#include<cmath>
#include<algorithm>
#include<functional>
#define max 200
using namespace std;
struct jcode//求得概率之后的结构体
{
    char c;
    int x;//概率
    int b;//下标
    friend bool operator<(jcode a, jcode b ){//优先队列排序
        return b.x<a.x;
    }
};
typedef jcode jjnum[max];
typedef struct bnode{//存储哈夫曼树的结构体数组
    char data;
    int  x;
    int  lchild,rchild,parent;
    char code[max];//每个字符编码
    int len;//编码长度
}bnode;
typedef bnode huffman[max];
//统计输入的字符及各自概率
int  getnum(string ss,jjnum str)
{
    int i,j;
    int temp[max];
    for(i = 0;i< max;i++)
    {
        temp[i]=0;
    }
    for(i = 0;i<ss.length();i++)//求概率
         temp[ss[i]-'!']++;
    j=0;
    for(i = 0;i<max;i++)//存到结构体1
    {
        if(temp[i]!=0)
        {
            str[j].x= temp[i];
            str[j].c = i+'!';
            str[j].b = j;
            j++;
        }
    }
    return j;
}
void create(priority_queue<jcode>q,huffman &t,int cnum,jjnum str)//构造哈夫曼树
{
    int i,a1,a2;
    for(i=0;i<2*cnum-1;i++)//初始化结点
    {
       t[i].lchild = t[i].parent = t[i].rchild = t[i].x  = 0;
       t[i].data= '1';

    }
    for(i=0;i<cnum;i++)//叶子节点
    {
        t[i].x = str[i].x;
        t[i].data = str[i].c;
    }

    for(i=cnum;i<2*cnum-1;i++)//根据哈夫曼编码构造哈夫曼树
    {
        a1 = q.top().b;
        t[a1].parent = i;q.pop();
        a2 = q.top().b;
        t[a2].parent = i;q.pop();
        t[i].lchild = a1;//指向孩子
        t[i].rchild = a2;
        t[i].x = t[a1].x+t[a2].x;//概率小的两个相加
        jcode e;
        e.c = '1';e.x = t[i].x;e.b = i;
        q.push(e);//和存到队列中
    }
    for(i=0;i<cnum;i++)//由0.1进行二元编码,1右0左
    {
        int x;
        x=i;
        t[i].len = 0;
        while(t[x].parent!=0)//从叶子节点向根
        {
            if(t[t[x].parent].lchild == x)
            {
                t[i].code[t[i].len]='0';//存下来编码
                t[i].len++;
            }
            else
            {
                t[i].code[t[i].len]='1';
                t[i].len++;
            }
            x = t[x].parent;

        }
    }

    for(i=0;i<cnum;i++)//输出每个字符编码
    {
        if(t[i].data!='1')
        {
          cout<<t[i].data<<"  ===  ";
          for(int j=t[i].len-1;j>=0;j--)
            cout<<t[i].code[j];
          cout<<endl;
        }
    }
}
//编码f
void strhttree(jjnum str,int num,huffman &t)
{
    int i;
    priority_queue<jcode>q2;
    for(i = 0; i < num; i++)//加入优先队列
        q2.push(str[i]);
    create(q2,t,num,str);
}
void yima(string ss,int i,huffman &t,int num,int cnum)//译码
{
    while(num>=cnum)//从根节点开始
    {
      if(ss[i]=='1')
      {
          num = t[num].rchild;
          i++;
      }
      else
     {
          num = t[num].lchild;
          i++;
      }
    }
    if(i<=ss.length())//得到一个叶子节点,即译出一个字符
    {
      cout<<t[num].data;
      yima(ss,i,t,2*cnum-2,cnum);//继续译码
    }
}
int main()
{
    string s;//输入字符串
    huffman t;//哈夫曼树
    getline(cin,s);
    jcode str[max];
    int cnum = getnum(s,str);//字符的种类个数
    strhttree(str,cnum,t);//构造哈夫曼树
    for(int k = 0;k<s.length();k++)//输出编码
        for(int i=0;i<cnum;i++)
       {
           if(s[k]==t[i].data){
             for(int j=t[i].len-1;j>=0;j--)
               cout<<t[i].code[j];
           }
       }
    cout<<endl;
    double sum1 =0.0,sum2 = 0.0;
    for(int i = 0;i<cnum;i++)//求编码效率
    {
       sum1+=((t[i].x*1.0/s.size())*(-log2(t[i].x*1.0/s.size())));
        sum2+=(t[i].x*1.0/s.size())*t[i].len;

    }
    cout<<"n=="<<(sum1/sum2)<<endl;
    string l;
    getline(cin,l);//输入码
    int x= 2*cnum -2;//根节点、
    yima(l,0,t,x,cnum);//译码
    return 0;
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值