首页 > 代码库 > 【POJ1521】【HDU1053】Entropy 哈夫曼(Huffman)编码

【POJ1521】【HDU1053】Entropy 哈夫曼(Huffman)编码

#include <stdio.h>
int main()
{
	puts("转载请注明出处谢谢");
	puts("http://blog.csdn.net/vmurder/article/details/43020921");
}


题意:

输出字符串的长度*8、huffman编码长度、两者比值。


题解:

huffman编码:

我们发现对于一个字符串,如果我们把它变成01串,比如ABCDE

那么我们需要

A : 000

B : 001

C : 010

D : 100

E : 101

来表示每一个字符,然后识别的时候就是每三个一识别。

这种编码叫定长编码。


显然对于一个串,它的定长编码长度是串长*定长。

但是其实它可以更短。


如果我们想要识别一个串,就需要所有字符的编码都不为其它编码的前缀,这样才可以成功解码。

而此时我们可以根据出现频率来构造huffman编码,以达到最优解。


构造方法可以见于黑书P245,毕竟它还是很水的,随便看看就能明白。


下面是我要讲的方法:

这种编码最后可以构成一颗二叉树,每个叶子节点都是一种字符,而它的变长编码(Huffman)就是从root到这个节点经过的边的序号序列,而左儿子边序号标0,右儿子边序号标1。


建树方法:先把所有字符按照出现次数作为关键字排序,然后给最小的两个赋一个父亲,然后把这两个叶子节点弹出优先队列,把父亲插入进去,父亲的关键字是儿子的size之和。

最后只剩下一个节点的时候停止。


证明在黑书上有,自己去翻吧,感性的思考一下就是每个点都有深度,然后∑深度*size=ans,

我们需要让size大的深度尽量小。


呃,虽然上面的证明显然是在chedan,但是,,并没有但是。


代码:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1000
using namespace std;
char s[N];
int cnt[N];
priority_queue<int>q;
int ans,len;
int main()
{
	freopen("test.in","r",stdin);
	int i,j,k;
	while(scanf("%s",s)!=EOF)
	{
		while(!q.empty())q.pop();
		len=strlen(s),ans=0;
		if(len==3&&s[0]=='E'&&s[1]=='N'&&s[2]=='D')return 0;
		memset(cnt,0,sizeof cnt);
		for(i=0;s[i];i++)cnt[s[i]]++;
		for(i=0;i<N;i++)if(cnt[i])q.push(-cnt[i]);
		for(;;)
		{
			i=q.top(),q.pop();
			if(q.empty())break;
			j=q.top(),q.pop();
			ans-=(i+j);
			q.push(i+j);
		}
		if(!ans)ans=len;
		printf("%d %d %.1lf\n",len*8,ans,(double)len*8/(double)ans);
	}
	return 0;
}


【POJ1521】【HDU1053】Entropy 哈夫曼(Huffman)编码