您的位置:首页 > 其它

优先队列解哈夫曼编码问题之带权路径长度

2016-03-30 23:39 281 查看

1.什么是优先队列?

先说个生活中的例子
想想医院,重症急诊患者肯定不能像普通患者那样依次排队就诊,他们就可以插队了
他比较迟进队列,但他优先级高,所以就相对较早出队列去就诊了

优先队列一般用堆来实现,堆有两种(具体看这:/article/9449681.html
对于大根堆实现的优先队列,总是优先级高的元素先被删除;相对的,对于小根堆实现的优先队列,总是优先级低的元素先被删除。

那么对于大根堆:你权值高,优先级也高
但对于小根堆,权值低,优先级才高,刚好相反
大根堆的实现在这:/article/9449681.html
下面用小根堆来解决解哈夫曼编码,其实就改几个符号(大于改小于号,大家可以对比一下)

2.先说什么是哈夫曼编码?

比如说AABCDCA这个字符串,我怎么编码呢
先统计个字符的出现次数(下面我把次数说成权值吧
A B
C D
3 1
2 1
然后构成一颗哈夫曼树:就权值最小的两个加起来,将加起来的权值作为他们两的父亲结点,把那两个剔除,将父亲结点加进来然后再把最小的加起来,作为父亲结点,直到最后只剩一个父亲结点(哎这里不怎么会说,直接看我制作的图吧)



那么4个字母就可以编码成
A:0
B:110
C:10
D:111
这样就将数据压缩了,本来我们每个用8个bit的二进制表示。

不过下面我们解决的是带权路径长度,就是取每个结点的权值乘以到根结点的长度(就是到根结点有多少条线),再将每个结点的计算记过加起来
以上面的例子为例:WPL = 1*3 +1 *3 + 2*2 + 3*1 = 13 ,
还有一种计算方法,就是把哈夫曼树除了根结点以外的索引权值加起来
我们从最低加起
WPL = 1 + 1 + 2 + 2 + 4 + 3 = 13
下面就是用第二中方法,看看编程实现,看看结果对不

3.解决哈夫曼编码问题之带权路径长度

#include<iostream>
using namespace std;
class Heap {
private:
int *data, size;
public:
Heap(int length_input) {
data = new int[length_input];
size = 0;
}
~Heap() {
delete[] data;
}
void push(int value) {
data[size] = value;
int current = size;
int father = (current - 1) / 2;
while (data[current] < data[father]) {
swap(data[current], data[father]);
current = father;
father = (current - 1) / 2;
}
size++;
}
int top() {
return data[0];
}
void update(int pos, int n) {
int lchild = 2 * pos + 1, rchild = 2 * pos + 2;
int min_value = pos;
if (lchild < n && data[lchild] < data[min_value]) {
min_value = lchild;
}
if (rchild < n && data[rchild] < data[min_value]) {
min_value = rchild;
}
if (min_value != pos) {
swap(data[pos], data[min_value]);
update(min_value, n);
}
}
void pop() {
swap(data[0], data[size - 1]);
size--;
update(0, size);
}
int heap_size() {
return size;
}
};
int main() {
//n:一共有n个数,value代表权值(在哈弗曼编码里代表每个字符出啊先的次数)
//ans:用于保存带权路径长度
int n, value, ans = 0;
cin>>n;
Heap heap(n);
for (int i = 1; i <= n; i++) {
cin>>value;
heap.push(value);
}
//因为我们的带权路径长度是不用算根结点的,所以下面的循环就是剩最后一个点时结束
//只有一个结点就直接它的权值就是带权路径长度
if (n == 1) {
ans = ans + heap.top();
}
while (heap.heap_size() > 1) {
//因为上面的数据结构是小根堆(即对于每个结点,它的权值小于以它为根构成的子树的所以的结点,注意这里不是之和)
//a,b都是从堆顶获取的权值,获取完就删除该结点,所以a,b就是堆里面权值最小的两个结点
int a = heap.top();
heap.pop();
int b = heap.top();
heap.pop();
//将a,b的和累加带权路径长度中
ans = ans + a + b;
//最后将a,b的和插入到堆中
heap.push(a+b);
}
cout<<ans<<endl;
return 0;
}

4.运行结果



可以看到跟我们上面的手算的结果是一样的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: