霍夫曼编码与priority_queue的千丝万缕

霍夫曼编码:用最短的二进制编码去对一个字符串编码

算法处理中,叠加当前最小的两个权值,然后再加入到权值集中正确的位置,这个过程就是一个优先级队列的实现。

算法实现:
1.频率排序:hash表统计频数,从小到大已排序好的权值(字符出现的频率)k[1],k[2]......k[i]....k[n],
2.头部求和再插入:比如,取k[1]+k[2]的值再插入到队列中。
3.重复循环步骤2,直到队列为空.

举例:

设一段文字中七个常用汉字为{的,地,得,和,个,在,是},每个字符的使用频率分别为{26,6,4,7,9,8,18}。请写出每个字符的哈夫曼编码(按照每个结点的左子树根结点的权小于等于右子树根结点的权的次序构造哈夫曼树,编码时规定:左0右1)



题目描述

请设计一个算法,给一个字符串进行二进制编码,使得编码后字符串的长度最短。

输入描述:
每组数据一行,为待编码的字符串。保证字符串长度小于等于1000。


输出描述:
一行输出最短的编码后长度。

输入例子:
MT-TECH-TEAM

输出例子:
33
解题思路:
假设这是个小根堆,然后堆中的数字是正数。可以用STL 中的priority_queue
就比如说这题的例子吧:将其统计之后为:A(1)H(1)C(1)E(2)-(2)M(2)T(3),字符后面的数字为其频数.
所以将其push进heap中之后,应该是1(A),1(H),1(C),2(E),2(-),2(M),3(T)
a、1,1出堆,ret+=1(A)+1(H),并且将1+1插入堆中,堆变成 1(C),2(E),2(-),2(M),2(AH),3(T)
b、1,2出堆,ret+=1(C)+2(E),并且将1+2插入堆中,堆变成 2(-),2(M),2(AH),3(T),3(CE)
c、2,2出堆,ret+=2(-)+2(M),并且将2+2插入堆中,堆变成 2(AH),3(T),3(CE),4(-M)
d、2,3出堆,ret+=2(AH)+3(T),并且将2+3插入堆中,堆变成 3(CE),4(-M),5(AHT)
e、3,4出堆,ret+=3(CE)+4(-M),并且将3+4插入堆中,堆变成 5(AHT),7(CE-M)
f、5,7出堆,ret+=5(AHT)+7(CE-M),此时退出循环
其实观察ret的值可知:
ret=1(A)+1(H)+1(C)+2(E)+2(-)+2(M)+2(AH)+3(T)+3(CE)+4(-M)+5(AHT)+7(CE-M)=33;
那为什么这样加起来就是huff编码之后该字符串的长度呢?
我们可以把 2(AH)拆成 1(A)+1(H),相应也可以把 4(-M)拆成 2(-)+2(M);
所以
ret=1(A)+1(H)+1(C)+2(E)+2(-)+2(M)+1(A)+1(H)+3(T)+1(C)+2(E)+2(-)+2(M)+1(A)+1(H)+3(T)+1(C)+2(E)+2(-)+2(M)
ret=1(A)*3+1(H)*3+1(C)*3+2(E)*3+2(-)*3+2(M)*3+3(T)*2=33;
而最后将AHCE-MT变成huff码之后,其长度刚好刚好为(3,3,3,3,3,3,2)(当然,处理相同频数的字符时,由于多样性,可以有不同的长度,但是最终结果还是33)。
所以
ret=字符1编码长度*频数+字符2编码长度*频数+...
=1(A)*3+1(H)*3+1(C)*3+2(E)*3+2(-)*3+2(M)*3+3(T)*2
=33;
所以直接这样做确实是正确的,因为是分析出来的,所以我们这里知道字符A的编码长度是3,而由heap是可以得到最长字符串编码总长度的,但是还是不能知道每个字符对应的huff码编码长度。


#include <iostream>
#include <string>
#include <queue>

using namespace std;

int main()
{
    string str;
    while(getline(cin,str)){
        priority_queue<int,vector<int>,greater<int>> heap;
        int hash[256]={0};
        for (int i = 0; i < str.size(); ++i) hash[str[i]]++;
        for (int i = 0; i < 256; ++i)
        {
            if(hash[i]>0)
                heap.push(hash[i]);
        }
        int res=0;
        while(heap.size()>1){
            int top1=heap.top();heap.pop();
            int top2=heap.top();heap.pop();
            res+=(top1+top2);
            heap.push(top1+top2);
        }
        cout<<res<<endl;
    }

    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值