这是在做一道编程提示遇到的,学习了一位博主的编码,其中有些问题未能理解,分析解决掉。
首先什么是哈夫曼树:
哈夫曼树,又称最优二叉树,是一类带权路径长度最短的树。
也就是根节点到节点的中的长度最小,当然条件就是,每条路径都是有权重的,
所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的 路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。树的带权路径长度记为WPL= (W1*L1+W2*L2+W3*L3+…+Wn*Ln)
此时WPL=32×1+24×2+2×3+7×3
一般建立哈夫曼树的步骤为
1,将所有左,右子树都为空的作为根节点。
2,在森林中选出两棵根节点的权值最小的树作为一棵新树的左,右子树,且置新树的附加根节点的权值为其左,右子树上根节点的权值之和。注意,左子树的权值应小于右子树的权值。
3,从森林中删除这两棵树,同时把新树加入到森林中。
4,重复2,3步骤,直到森林中只有一棵树为止,此树便是哈夫曼树。
太原理工网站给出了动画演示
http://www.tyut.edu.cn/kecheng1/site01/suanfayanshi/Huffman.asp
上面提到的根据权重排序,选出权重最小的两个,这个功能在优先队列中完全可以做到。所以在构建哈夫曼树时可以利用优先队列
然后看看题目吧
Input
The input file will contain a list of text strings, one per line. The text strings will consist only of uppercase alphanumeric characters and underscores (which are used in place of spaces). The end of the input will be signalled by a line containing only the word “END” as the text string. This line should not be processed.
Output
For each text string in the input, output the length in bits of the 8-bit ASCII encoding, the length in bits of an optimal prefix-free variable-length encoding, and the compression ratio accurate to one decimal point.
Sample Input
AAAAABCD
THE_CAT_IN_THE_HAT
END
Sample Output
64 13 4.9
144 51 2.8
这里直接给出网上参考代码,然后分析
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<functional>
#include<queue>
using namespace std;
#define M 1000050
char str[M]; //全局变量中 默认初始化为0;
int list[27];
priority_queue< int,vector<int>,greater<int> >que;
int main()
{
int ans,sum;
int i,a,b,c;
while(scanf("%s",str),strcmp(str,"END")){
memset(list,0,sizeof(list));
for(i=0;str[i];i++){
if(isalpha(str[i]))
list[str[i]-‘A‘]++;
else
list[26]++;
}
sum=i*8;ans=i;c=0; //sum 为原等长编码需要的bit位 ans为hfm编码
for(i=0;i<27;i++){
if(list[i]){
que.push(list[i]);
c++;
}
}
if(c>1) //c==1时,只有一种字母
{
ans=0;//注意只有一种字符的情况
while(que.size()!=1)
{
a=que.top();
que.pop();
b=que.top();
que.pop();
ans+=a+b;
que.push(a+b);
}
while(!que.empty())//使用后清空队列
que.pop();
}
printf("%d %d %.1f\n",sum,ans,1.0*sum/ans);
}
return 0;
}
1、输入字符串部分
for(i=0;str[i];i++){
if(isalpha(str[i]))
list[str[i]-‘A‘]++;
else
list[26]++;
}
在ctype.h中,是一个宏,判读是否为大写字母
list是一个数组int list[27]统计26个字母和下划线字符,用来统计多少个A、B,用字母到A的绝对距离作为数组的下标,数组对应的元素存放字母出现
的次数。这里的写法非常简洁,数组元素++的写法,
2、编码计数
sum=i*8;ans=i;c=0;
sum 为原等长编码需要的bit位 ans为hfm编码,i为字母的个数
3、利用优先队列来考虑权重问题
for(i=0;i<27;i++){
if(list[i]){
que.push(list[i]);
c++;
}
}
将字母出现的次数作为权重,压如队列中,C用于记录出现不同字母的个数。
3、模拟建立哈夫曼树
if(c>1) //c==1时,只有一种字母
{
ans=0;//注意只有一种字符的情况
while(que.size()!=1)
{
a=que.top();
que.pop();
b=que.top();
que.pop();
ans+=a+b;
que.push(a+b);
}
while(!que.empty())//使用后清空队列
que.pop();
}
while中的过程完全按照,上面提到的步骤来
如果加入输入AAAABBBCCD,根据上面步骤会得到这样一棵树
这样编码出来为
A: 0 1bit
B: 10 2bit
C: 110 3bit
D: 111 3bit
所以中的编码位数就是出现次数×编码bit
1×4+2×3+3×2+3×1=19
这个就是带权路径长度,因为出现的次数就是权重,编码长度就是节点到根节点的层数,
如何在不把树建立起来,求带权路径长度,只要将这些权重全部加起来即可,正如程序中所做的那样
程序中
ans=(1+2)+(3+3)+(4+6);
这样分解出来
(1+2)+((1+2)+3)+(4+((1+2)+3))
将(1+2)加了3次,实际就是加的层数。
所以ans就是这个哈夫曼树的带权路径和。
版权声明:本文为博主原创文章,未经博主允许不得转载。