优先队列解哈夫曼编码问题之带权路径长度
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.运行结果
可以看到跟我们上面的手算的结果是一样的
相关文章推荐
- C++实验二—两点间距
- 数据库包装成类
- 1-3 冯诺依曼结构的小故事
- 对象的实现
- SCanDroid静态分析工具使用(一)安装配置
- Linux交叉编译的时候,加载freetype动态库的时候出现了问题,(已解决)
- 芯片制造良率分析系统YMS
- Day of Week
- ListView、GridView与ScrollView嵌套冲突解决
- 库
- MyBatis学习笔记(一)
- 对象的实现
- I/O多路复用技术
- 码农小汪-剑指Offer之5 -替换空格
- Android Studio快捷键
- 转 - markdown 简明语法
- 软件设计之旅(1) 初写软件需求规格说明
- C++最常见面试题解答
- 20160329个票分析:柳毅加标
- 2016/3/30 租房子 ①建立租房子的增、删、改php页面 ②多条件查询 ③全选时 各部分全选中 任意checkbox不选中 全选checkbox不选中