贪心算法 赫夫曼编码问题(Huffman)

赫夫曼编码是一种广泛用于数据压缩的问题,该算法的主要优势在于节约了存储和传输成本。
举一个例子:
假设要传输的数据为

贪心算法 赫夫曼编码问题(Huffman)_第1张图片

那么传输成本就是:
45*3 + 30 * 3 + 29 * 3 + 10 * 3 + 8 * 3 + 5 * 3 = 381个字符

我们可以使用赫夫曼编码思想来解决

  1. 先合并最小频率的2个字符对应的子树,计算合并后的子树的频率;
  2. 重新排序各个子树;
  3. 重复步骤1
  4. 重复步骤2
  5. 对二叉树中的边赋予0、1,得到各字符的变长编码。

对于上举的例子而言就是:
EF最小,首先构造EF的生成树,重新排序

贪心算法 赫夫曼编码问题(Huffman)_第2张图片

构造EF 和 D的生成树,重新排序

贪心算法 赫夫曼编码问题(Huffman)_第3张图片

构造EFD 和 C 的生成树,重新排序

贪心算法 赫夫曼编码问题(Huffman)_第4张图片

构造EFDC 和 B 的生成树,重新排序

贪心算法 赫夫曼编码问题(Huffman)_第5张图片

构造EFDCB 和 A 的生成树,重新排序

贪心算法 赫夫曼编码问题(Huffman)_第6张图片

赫夫曼编码后的二进制数据为:

贪心算法 赫夫曼编码问题(Huffman)_第7张图片

可以看见,利用赫夫曼思想设计之后,频率高的字符,二进制码短了,频率低的字符,二进制码长了,这样就有效得减少了总得二进制码数。

那么传输成本就是:
45*1 + 30 * 2 + 29 * 3 + 10 * 4 + 8 * 5 + 5 * 5 = 292个字符,节约了23%的成本!

设B为总编码长度,C为字符集,f(c)为对应字符的频率,d(c)为字符在赫夫曼树的深度,则有:

这里写图片描述

实例讲解

把一块无限长的木板锯成几块给定长度的小木板,每次锯都需要一定费用,费用就是当前锯的木板的长度。给定各个要求的小木板的长度以及小木板的个数,求最小的费用。利用Huffman思想,要使总费用最小,那么每次只选取最小长度的两块木板相加,再把这些和累加到总费用中即可。
以需要3块长度分别为8,10,4的木板为例,首先4和8形成一个生成树权值为12,接着10和12形成生成树,权值为22。因此一开始砍一个长度为22的木板,费用为22;接着砍4,费用总和为26,;接着砍8,费用总为34;
为了提高效率,使用优先队列优化,并且还要注意使用long long int保存结果。

#include  
#include  
#include  
using namespace std;  

int main(){
    long long int sum = 0;
    int n,t,a,b;
    cin >> n;
    priority_queue<int,vector<int>,greater<int> > q;
    for(int i = 0; i < n; i++){
        cin >> t;
        q.push(t);
    }
    if(q.size() == 1){
        sum += q.top();
        q.pop();
    }
    while(q.size() > 1){
        a = q.top();
        q.pop();
        b = q.top();
        q.pop();        
        sum += (a+b);
        q.push(sum);
    }

    cout << sum;
}

你可能感兴趣的:(算法—贪心算法)