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

1.什么是优先队列?


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

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

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

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


比如说AABCDCA这个字符串,我怎么编码呢
先统计个字符的出现次数(下面我把次数说成权值吧
A 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.运行结果



可以看到跟我们上面的手算的结果是一样的

posted @ 2016-03-30 23:39  SEC.VIP_网络安全服务  阅读(242)  评论(0编辑  收藏  举报