数据结构实验之二叉树六:哈夫曼编码

数据结构实验之二叉树六:哈夫曼编码

Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^

题目描述

字符的编码方式有多种,除了大家熟悉的ASCII编码,哈夫曼编码(Huffman Coding)也是一种编码方式,它是可变字长编码。该方法完全依据字符出现概率来构造出平均长度最短的编码,称之为最优编码。哈夫曼编码常被用于数据文件压缩中,其压缩率通常在20%90%之间。你的任务是对从键盘输入的一个字符串求出它的ASCII编码长度和哈夫曼编码长度的比值。

输入

  输入数据有多组,每组数据一行,表示要编码的字符串。

输出

  对应字符的 ASCII 编码长度 la huffman 编码长度 lh la/lh 的值 ( 保留一位小数 ) ,数据之间以空格间隔。

示例输入

AAAAABCD
THE_CAT_IN_THE_HAT

示例输出

64 13 4.9
144 51 2.8

提示


 这里先讲c++中使用STL优先队列的方法模拟哈夫曼编码。

用法:

示例:将元素5,3,2,4,6依次push到优先队列中,print其输出。

1. 标准库默认使用元素类型的<操作符来确定它们之间的优先级关系。

priority_queue<int> pq;

通过<操作符可知在整数中元素大的优先级高。
故示例1中输出结果为: 6 5 4 3 2

 

2. 数据越小,优先级越高

priority_queue<int, vector<int>, greater<int> >pq; 

其中
第二个参数为容器类型。
第三个参数为比较函数。

greater<T>

表示T类型>运算。


和代码中的自定义cmp函数效果一样。

函数对象
故示例2中输出结果为:2 3 4 5 6

3. 自定义优先级,重载比较符号

重载默认的 < 符号

复制代码
struct node
{
    friend bool operator< (node n1, node n2)
    {
        return n1.priority < n2.priority;
    }
    int priority;
    int value;
}; 
复制代码

 priority_queue<node> qn;

这时,需要为每个元素自定义一个优先级。

注:重载>号会编译出错,因为标准库默认使用元素类型的<操作符来确定它们之间的优先级关系。
而且自定义类型的<操作符与>操作符并无直接联系。


#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
char a[10000];
int b[1000];
struct cmp
{
    bool operator()(const int &a,const int &b)  //cmp重载了()运算符
    {
        return a>b;
    }
};
int main()
{
    int i,j,n,m,k,t,x1,x2;
    priority_queue<int,vector<int>,cmp>que;  //priority_queue<int ,vector<int>,greater<int> >que;
    while(scanf("%s",a)!=EOF)
    {
        k=0;
        memset(b,0,sizeof(b));
        n=strlen(a);
        m=n*8;
        for(i=0;i<n;i++)
            b[a[i]]++;
        for(i=0;i<150;i++)
           if(b[i]!=0)
            que.push(b[i]);
        while(!que.empty())
        {
           x1=que.top();
           que.pop();
           if(!que.empty())
           {
               x2=que.top();
           que.pop();
           k+=(x1+x2);
           printf("%d ",k);
           que.push(x1+x2);
           }
        }
        printf("%d %d %.1lf\n",m,k,1.0*m/k);
        }
}

下面是采用创建哈夫曼树计算哈夫曼编码。

其中注释吊的是另一种统计字符个数的方法,可能会稍微耗时,但是没注释掉的却多开辟了内存。

#include<cstdio>
#include<cstring>
#include<limits.h>            //INT_MAX 的头文件
#define Max INT_MAX
using namespace std;
int j;         // 统计不同字符个数
struct Tnode
{
    char d;
    int w;          //权值
    int l,r,p;      //左右孩子、双亲节点
};
void CreatHT(Tnode ht[],char st[])   //创建哈夫曼树
{
    int  n=strlen(st);
    j=0;
    int m=2*n-1;
    for(int i=0; i<m; i++)          //赋初值,会多
    {
        ht[i].w=0;
        ht[i].l=-1;
        ht[i].r=-1;
        ht[i].p=-1;
    }

//    for(int i=0; i<n; i++)      //统计每个字符的个数
//    {
//        int flag=0;
//        for(int k=0; k<j; k++)
//            if(ht[k].d==st[i])
//            {
//                flag=1;
//                ht[k].w++;
//                break;
//            }
//        if(!flag)
//        {
//
//            ht[j].d=st[i];
//            ht[j].w++;
//            j++;
//        }
//    }
    int a[1000];
    memset(a,0,sizeof(a));
    for(int i=0; i<n; i++)
        a[st[i]]++;
    for(int i=0; i<130; i++)
        if(a[i]!=0)
        {
            ht[j++].w=a[i];
        }

    for(int i=j; i<j*2-1; i++)
    {
        int min1=Max;
        int min2=Max;
        int l=-1;
        int r=-1;
        for(int k=0; k<i; k++)      //寻找当前数组中最小的两个节点
        {
            if(ht[k].p==-1)            //在尚未构建的节点中查找
                if(ht[k].w<min1)
                {
                    min2=min1;         //将上一节点: 权值赋值到min2,孩子节点赋值r
                    r=l;
                    min1=ht[k].w;       //赋值当前节点
                    l=k;
                }
                else if(ht[k].w<min2)
                {
                    min2=ht[k].w;
                    r=k;
                }
        }
        ht[i].w=ht[r].w+ht[l].w;
        ht[i].l=l;
        ht[i].r=r;
        ht[l].p=i;
        ht[r].p=i;
    }
}
int  Print(Tnode ht[])
{
    int sum=0;              //计算权值
    for(int i=j; i<j*2-1; i++)
        sum+=ht[i].w;
    return sum;
}
int main()
{
    char st[1000];
    while(~scanf("%s",st))
    {
        Tnode ht[2000];
        int x=strlen(st);
        x*=8;               //每个ASCII码8个二进制数
        CreatHT(ht,st);
        int y=Print(ht);
        printf("%d %d %.1lf\n",x,y,1.0*x/y);
    }
}


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值