赫夫曼编码是一种广泛用于数据压缩的问题,该算法的主要优势在于节约了存储和传输成本。
举一个例子:
假设要传输的数据为
那么传输成本就是:
45*3 + 30 * 3 + 29 * 3 + 10 * 3 + 8 * 3 + 5 * 3 = 381个字符
我们可以使用赫夫曼编码思想来解决
- 先合并最小频率的2个字符对应的子树,计算合并后的子树的频率;
- 重新排序各个子树;
- 重复步骤1
- 重复步骤2
- 对二叉树中的边赋予0、1,得到各字符的变长编码。
对于上举的例子而言就是:
EF最小,首先构造EF的生成树,重新排序
构造EF 和 D的生成树,重新排序
构造EFD 和 C 的生成树,重新排序
构造EFDC 和 B 的生成树,重新排序
构造EFDCB 和 A 的生成树,重新排序
赫夫曼编码后的二进制数据为:
可以看见,利用赫夫曼思想设计之后,频率高的字符,二进制码短了,频率低的字符,二进制码长了,这样就有效得减少了总得二进制码数。
那么传输成本就是:
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<queue>
#include<cstdio>
#include<iostream>
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;
}