您的位置:首页 > 产品设计 > UI/UE

STL的priority_queue及其背后的heap读书笔记

2017-07-20 14:46 225 查看
今天继续记录一下优先队列(priority_queue)的感想。其实优先队列的本质就是一个堆,STL中默认是大根堆(大顶堆)。在学习数据结构的堆排序的时候,已经接触过堆的相关性质了,这里就不展开讲堆了。

虽然在逻辑结构上堆是一个完全二叉树,但在实现上时经常是用数组来表达数据结构。基于此,STL的优先队列也是一个容器适配器,默认是在vector的基础上封装而来的。

但要注意一下STL中的堆是以下标0的元素作为根的,所以左右子树的计算规则,是对于任意元素
i
,若有左子树则左子树的下标为
2*i+1
,右子树的下标为
2*i+2


下面贴上优先队列的模板,优先队列是定义在头文件queue上的,使用的时候需要

包含

#include<queue>


优先队列的模板参数

模板中分别是三个参数,数据类型,底层数据结构(默认vector),比较规则(默认升序,是一个函数对象)



成员方法

成员方法和构造函数如下,适合stack的成员方法类似的,但是内在的东西是不同的,应该有人发现了里面的push方法调用了pop_heap()方法,构造函数里面调用了make_heap()方法。

成员方法



构造函数



没错了,其实优先队列的背后是通过一系列泛型算法来对一个vector进行操作使之具有堆的结构。下面先贴上priority_queue的简单使用,用起来非常方便,然后在进入本文的重点构造heap的泛型算法,make_heap等函数。

代码示例

int data[]={1,3,2,5,6,4,9,8,7,10};
priority_queue<int>  p_queue;
for(int i=0;i<sizeof(data)/sizeof(*data);++i)
{
p_queue.push(data[i]);//输入数据
}

cout<<"输出堆顶元素:";
for(int i=0;i<sizeof(data)/sizeof(*data);++i)
{
cout<<p_queue.top()<<" ";//输出:10 9 8 7 6 5 4 3 2 1
p_queue.pop();
}


建立堆的泛型算法

因为优先队列的底层是vector,测试代码用的也是vector.

push_heap()



因为用了迭代器,所以可读性真的不友好,默认的新插入的元素是位于数组的最后一个,
__parent
是新堆(完全二叉树)的最后一个非叶子节点。然后while循环里面不断的对新插入的值做堆的筛选。还是放图比较清晰:



书上的详细过程实际是对以插入值(50)为最后一个叶子节点的子完全二叉树的最后一个非叶子节点做筛选工作,直到新插入值所在的位置使得整棵树是一个堆。说起来比较绕口。拿上图来说,右上角的树只看被红圈圈出来的子树,然后对这个子树第一步筛选,如新插入值成为了父节点的话,就继续到达右下角图的步骤。

make_heap()

make_heap()方法是对一个vector进行建堆操作。具体如下:



里面的
__adjust_heap()
则是对当前节点自上往下的进行堆的更新(筛选)操作。



pop_heap()

pop_heap()的具体流程也是调用了
__adjust_heap()
,只不过序列长度减1

假设我们现在有一个大小为n的堆,
pop_heap()
操作则是将第0个元素和最后一个元素交换,然后对剩余的
0~n-2
序列进行建立堆,更严格的来说是对交换后的第0个元素进行筛选操作

要注意的是使用泛型算法的pop_heap操作的vector并没有真正把堆顶元素移除出序列,只是放在了vector的末尾。而优先队列(priority_queue的pop操作则是移除的了)。下面看区别



priority_queue的pop操作:

先调用了pop_heap(),底层容器在执行pop_back()操作。



sort_heap()

这个就是堆排序的算法了,总的来说操作就是不断的把根节点和”当前堆”的最后一个节点交换,然后重新建堆,相当于不断的进行pop_heap操作。因为后面打算在单独写一篇堆排序的文章,这里就不就STL的算法源码写太多,其实操作的手法都差不多的。



下面贴上一下只用堆的泛型算法来建立堆的过程:

vector<int>  heap={1,2,3,4,5,6,7,8,9};

make_heap(heap.begin(),heap.end());//建立堆

sort_heap(heap.begin(),heap.end());//堆排序

cout<<endl<<"输出堆排序结果:";

for(auto it:heap)
{
cout<<it<<" ";
}

make_heap(heap.begin(),heap.end());//重新建立堆
pop_heap(heap.begin(),heap.end());//pop出堆顶元素
//不进行真正的pop

cout<<endl<<"输出重新建立的堆,并且没有真正pop堆顶结果:";
for(auto it:heap)
{
cout<<it<<" ";
}


输出结果:



好了,关于STL的heap暂时就记录到这里了

PS:您的指正,是我的荣幸~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息