priority_queue的底层是用vector实现的,因此严格意义上讲,priority_queue属于容器适配器。在介绍序列式容器之前,先介绍heap。heap的定义(以大根heap为例)是递归定义:max heap或是一棵空树 或是具有以下性质:max heap的左右子树的所有节点都是小于根节点的,其左子树和右子树也是max heap。
heap就是一种完全二叉树,也就是说,整棵树除了底层的叶节点之外,都是满的,而最底层的叶节点从左向右不得有空隙。
完全二叉树整棵树内没有任何节点漏掉,这带来一个极大的好处:我们可以利用array来存储所有节点。假设用一个小技巧,将array的#0元素保留,那么完全二叉树中的某个节点位于array的i处时,其左子节点必定位于array的2i处,其右子节点必位于array的2i + 1处,其父节点必位于“i/2”处。通过这种规则可以轻易的实现出完全二叉树,但是heap需要增加或减小大小,array完成不了,所以使用vector来代替。所以实现priority_queue只需要一个vector和一组heap算法即可。
- push_heap(Iterator first, Iterator last)算法
first,last表示vector的首尾,调用函数push_back之前,已经把要插入的元素放到了vector的尾部(此时最后一个元素不一定符合heap的性质),执行完之后,整个vector都符合heap的性质。
为满足完全二叉树的条件,新加入的元素一定要放在最下一层作为叶节点,并填补在由左至右的第一个空格,也就是把新元素插入在底层vector的end()处。新元素是否适合于当前的位置呢?以max heap为例,为了满足max heap的条件,我们执行一个所谓的上溯程序:将新节点拿来与其父节点比较,如果key比父节点的大,就父子对换位置。如此一直上溯,直到不需要对换或直到根节点为止。
//max heap的源码
template <typename RandomAccessIterator>
inline void push_heap(RandomAccessIterator first, RandomAccessIterator last){
//注意,此函数被调用时,新元素应已置于底部容器的最尾端
__push_heap_aux(first, last, distance_type(first), value_type(first));
}
template <typename RandomAccessIterator, typename Distance, typename T>
inline void __push_heap_aux(RandomAccessIterator first, RandomAccessIterator last,
Distance*, T*){
__push_heap(first, Distance((last - first) - 1), Distance(0), T(*(last - 1)));
//新值必置于底部
}
template <typename RandomAccessIterator, typename Distance, typename T>
void __push_heap(RandomAccessIterator first, Distance holeIndex, Distance topIndex, T value){
Distance parent = (holeIndex - 1) / 2;//找出父节点
while(holeIndex > topIndex && *(first + parent) < value){
//尚未到达顶端,且父节点小于新值(于是不符合heap的次序特性)
*(first + holeIndex) = *(first + parent);
holeIndex = parent;
parent = (holeIndex - 1) / 2;
}
*(first + holeIndex) = value;//完成插入操作
}
- pop_heap(Iterator first, Iterator last)算法
first,last表示vector的首尾,以max heap为例,执行pop_heap()函数之后,最大的元素放到了vector的尾部,注意这个时候并没有把这个元素释放掉,如果需要释放,调用vector的pop_back(),如果要显示,调用back()函数。
操作的步骤是:把vector最后一个元素放到#0(哨兵位置);把veator的头元素,放到vector尾部(如果想删除掉,执行pop_back()即可);然后执行下溯程序:将空间节点和其较大子节点“对调”,直到找到合适的位置,到此才恢复了max heap的性质。
//最核心的调整算法,将value向下调整
template <typename RandomAccessIterator, typename Distance, typename T>
void __adjust_heap(RandomAccessIterator first, Distance holeIndex, Distance len, T value){
Distance topindex = holeIndex;
Distance maxChild = 2 * holeIndex + 1;//洞结点之左结点
while(maxChild < len){
//比较洞结点之左右两个值,然后以maxChild代表较大子节点
if((maxChild + 1) < len && *(first + maxChild) < *(first + maxChild + 1)) ++maxChild;
if(*(first + maxChild) < value) break;//已找到合适的位置
*(first + holeIndex) = *(first + maxChild);
holeIndex = maxChild;
maxChild = 2 * holeIndex + 1;
}
*(first + holeIndex) = value;
}
- sort_heap(Iterator first, Iterator last)算法
first,last表示vector的首尾,如果是对max heap执行这个函数,最终得到的vector是递增的,如果是对min heap执行这个函数,最终得到的vector是递减的,这个函数的内部实现是循环调用pop_heap函数。
- make_heap(Iterator first, Iterator last)算法
first,last表示vector的首尾,这个算法用来将一段现有的数据转化为一个heap,内部实现是多次调用调整函数。
template <typename RandomAAccessIterator, typename T, typename Distance>
void __make_heap(RandomAAccessIterator first, RandomAAccessIterator last, T*, Distance*){
if(last - first < 2) return; //长度为1或0不必排序
Distance len = last - first;
//找出第一个需要重新排列的子树的头
Distance parent = (len - 2) / 2;
while(true){
__adjust_heap(first, parent, len, T(*(first + parent)));
if(parent == 0) return;//走完根节点,就结束
parent--;
}
}
priority_queue
priority_queue使用heap实现的,因为heap没有迭代器,所以priority_queue也没有迭代器,不提供遍历操作。priority_queue的底层是用vector实现的,因此严格意义上讲,priority_queue属于容器适配器。
template <typename T, typename Sequence = vector<T>, typename Compare = less<typename Sequence::value_type>>
class priority_queue{
public:
typedef typename Sequence::value_type value_type;
typedef typename Sequence::size_type size_type;
typedef typename Sequence::reference reference;
typedef typename Sequence::const_reference const_reference;
protected:
Sequence c; //底层容器
Compare comp;//元素比较标准
public:
priority_queue():c(){}
explicit priority_queue(const Compare& x):c(),comp(x) {}
//以下用到的make_heap(),push_heap(),pop_heap()都是泛型算法
//注意下面的构造函数中InputIterator是指存放数据(使用这个数据建立priority_queue)的容器的迭代器
template <typename InputIterator>
priority_queue(InputIterator first, InputIterator last, const Compare& x):c(first, last),comp(x){make_heap(c.begin(), c.end(), comp);}
priority_queue(InputIterator first, InputIterator last):c(first, last){make_heap(c.begin(), c.end());}
bool empty() const {return c.empty();}
size_type size() const {return c.size();}
const_reference top() const {return c.front();}
void push(const value_type& x){
__STL_TRY{
//push_back是泛型算法,先把元素放到vector的底端,在排序
c.push_back(x);
push_heap(c.begin(), c.end(), comp);
}
__STL_UNWIND(c.clear());
}
void pop(const value_type& x){
__STL_TRY{
//pop_back是泛型算法,从heap内取出一个元素。它并不是真正将元素弹出,而是重排heap
//然后再以底层容器的pop_back()将元素弹出
pop_heap(c.begin(), c.end(), comp);
c.pop_back(x);
}
__STL_UNWIND(c.clear());
}
}