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

 哈夫曼编码(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;
}



  • 5
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
哈夫曼编码是一种无损压缩算法,通过将频率较高的字符用较短的编码表示,频率较低的字符用较长的编码表示,从而减少了信息的传输量,达到了压缩数据的目的。 下面以一个简单的例子来说明哈夫曼编码的过程。 假设有一个文本文件,其中包含以下字符及其出现频率: 字符 | 频率 ----|---- A | 20 B | 15 C | 10 D | 5 首先,将字符按照出现频率从高到低排序,得到如下表格: 字符 | 频率 ----|---- A | 20 B | 15 C | 10 D | 5 接下来,构建哈夫曼树。具体的构建过程是,将出现频率最小的两个字符作为叶子节点,并将它们的和作为它们的父节点的频率,然后将这个父节点再与下一个出现频率最小的字符一起构成一个新的节点。重复这个过程直到所有的字符都被构建成了一颗树。这个过程的具体实现可以使用优先队列或者堆等数据结构来进行。 在本例中,构建哈夫曼树的过程如下所示: ``` +----+ | 50 | +----+ / \ / \ / \ +----+ +----+ | A | | | | 20 | | | +----+ | | | 30 | | | +-----+ / \ / \ / \ +----+ +----+ | B | | C | | 15 | | 10 | +----+ +----+ / \ / \ / \ +----+ +----+ | D | | | | 5 | | | +----+ | | | 5 | | | +----+ ``` 最后,对哈夫曼树进行编码。对于每个叶子节点,从根节点出发,如果向左走则录一个0,向右走则录一个1。最终得到每个字符的哈夫曼编码。在本例中,字符A、B、C和D的哈夫曼编码分别为: 字符 | 频率 | 哈夫曼编码 ----|----|------ A | 20 | 0 B | 15 | 10 C | 10 | 110 D | 5 | 111 可以看到,由于A出现的频率最高,所以它的编码最短,只需一个0即可表示。而由于D出现的频率最低,它的编码最长,需要三个1才能表示。 使用哈夫曼编码后,原来的文本文件可以被压缩为: ``` 10110111011100 ``` 这个字符串中共有20个0和15个1,总共35个二进制位。如果每个字符用一个8位的ASCII码表示,那么原来的文本文件大小为8×(20+15+10+5) = 400位。因此,使用哈夫曼编码后,压缩率为35÷400 ≈ 0.0875,即压缩了约91.25%的数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值