数据结构实验之二叉树六:哈夫曼编码
Time Limit: 1000 ms Memory Limit: 65536 KiB
Problem Description
字符的编码方式有多种,除了大家熟悉的ASCII编码,哈夫曼编码(Huffman Coding)也是一种编码方式,它是可变字长编码。该方法完全依据字符出现概率来构造出平均长度最短的编码,称之为最优编码。哈夫曼编码常被用于数据文件压缩中,其压缩率通常在20%~90%之间。你的任务是对从键盘输入的一个字符串求出它的ASCII编码长度和哈夫曼编码长度的比值。
Input
输入数据有多组,每组数据一行,表示要编码的字符串。
Output
对应字符的ASCII编码长度la,huffman编码长度lh和la/lh的值(保留一位小数),数据之间以空格间隔。
Sample Input
AAAAABCD
THE_CAT_IN_THE_HAT
Sample Output
64 13 4.9
144 51 2.8
首先说一下哈夫曼编码
设某信源产生有五种符号u1、u2、u3、u4和u5,对应概率P1=0.4,P2=0.1,P3=P4=0.2,P5=0.1。首先,将符号按照概率由大到小排队,如图所示。编码时,从最小概率的两个符号开始,可选其中一个支路为0,另一支路为1。这里,我们选上支路为0,下支路为1。再将已编码的两支路的概率合并,并重新排队。多次重复使用上述方法直至合并概率归一时为止。
- 哈夫曼编码_百度百科
说白了哈夫曼树就是离散数学中的最优二叉树。那么根据最优二叉树的建成方法来用代码实现就可以了。
每次取最小的两个节点,合成一个新的节点,直到节点为一为止,一棵由m个带权节点的最优二叉树含有(2*m-1)个节点。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*这里用数组模拟二叉树来完成哈夫曼编码(三叉链表有点麻烦,挖个坑,有机会补上)*/
/*w表示出现的次数,l左儿子的序号,r右儿子的序号,p父亲节点的序号*/
struct tree
{
char data;
int w,l,r,p;
}s[100050*4];
int INF = 1e9+7;/*等价于 int INF = 1000000007 相当于一个无穷大的数*/
int main()
{
char ss[100050];
int i,j,num,m,n,w1,w2,k1,k2,p,sum,f[100050];
while(scanf("%s",ss)!=EOF)
{
memset(f,0,sizeof(f));
n = strlen(ss);
m = 0;
/*输入并且统计每个字母出现的次数,即带权节点*/
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
if(s[j].data==ss[i])
{
s[j].w++;
break;
}
}
if(j==m)
{
s[m].data = ss[i];
s[m].w = 1;
s[m].l = s[m].r = s[m].p = -1;
m++;
}
}
/*把剩余的节点写出来*/
for(i=m;i<2*m-1;i++)
{
/*记录两个最小权值*/
w1 = w2 = INF;
/*记录两个最小权值的编号*/
k1 = k2 = -1;
/*查询两个最小权值*/
for(j=0;j<i;j++)
{
if(f[j])
continue;
if(s[j].w<w1)
{
w2 = w1;
k2 = k1;
w1 = s[j].w;
k1 = j;
}
else if(s[j].w<w2)
{
w2 = s[j].w;
k2 = j;
}
}
f[k1] = f[k2] = 1;
/*建立一个新的节点*/
s[i].w = s[k1].w + s[k2].w;
s[i].l = k1;
s[i].r = k2;
s[i].p = -1;
s[k1].p = s[k2].p = i;
}
sum = 0;
/*计算哈夫曼树的编码长度(带权节点的权值*路径长度)*/
for(i=0;i<m;i++)
{
num = 0;
p = s[i].p;
while(p!=-1)
{
num++;
p = s[p].p;
}
sum += num * s[i].w;
}
printf("%d %d %.1f
",n*8,sum,8.0*n/sum);
}
return 0;
}