heap并不属于STL容器组件,它是个幕后英雄,扮演priority queue的助手。顾名思义,priority queue允许用户以任何次序将元素推入容器内,但取出来时一定是从优先权最高的元素开始取。
list可以作为priority queue的底层机制,元素插入操作可享常数时间。但是找到list的最大值,却需要对整个list进行线性扫描。可以改变做法,元素插入前先经过排序一关,使得list的元素值总是由小到大(或由大到小),这样一来,取极值和删除可以达到常数时间,但是插入确是线性时间。
RB-tree可以实现priority queue的底层机制。元素的插入和极值的取得就是O(lgn)的表现,但是杀鸡用牛刀,未免小题大做,一来binary search tree的输入需要满足足够的随机性,二来binary search tree并不容易实现。priority queue的复杂度,最高介于queue和binary search tree之间,这样, binary heap便是这种条件最好下最好的候选人。
现在分别介绍push_heap,pop_heap,sort_heap,make_heap相关算法。
push_heap:
为了满足complete binary tree的条件,新加入的元素一定要放在最下一层作为叶子节点,并填补在由左至右的第一个空格,也就是把新元素插入到底层的vector的end()处。新元素是否合适于其现有位置呢?为满足max-heap的条件,执行一次所谓的precolate up程序:将新节点拿来与其父节点比较,如果其键值比父节点大,就父子交换位置,如此一直上溯,直到不需要对换或直到根节点为止。示意图如下:
下面便是push_heap算法的实现细节。该函数接受两个迭代器,用来表示一个heap底部容器(vector)的头尾,并且新元素已经插入底部容器的最尾端。如果不符合这两个条件,push_heap的执行结果未可预知。
template
inline void push_heap(RandomAccessIterator first, RandomAccessIterator last){
//注意,此函数被调用时,新元素应已置于底部容器的最尾部
__push_heap_aux(first, last, distance_type(first), value_type(first));
}
template
inline void __push_heap_aux(RandomAccessIterator first, RandomAccessIterator last, Distance*, T*)
{
__push_heap(first, Distance((last-first)-1), Distance(0), T(*(last-1)));
//以上系根据implicit representation heap的结构特性:新值必置于底部容器的最尾端,此即第一个洞号:(last-first)-1
}
template
void __push_heap(RandomAccessIterator first, Distance holeIndex, Distance topIndex, T value)
{
//当尚未到达顶端,且父节点小于新值
//由于以上使用operator <, 可知STL heap是一种max-heap
Distance parent = (holeIndex-1)/2;
while(holeIndex>topIndex&&*(first+parent)
pop_heap:
max-heap,最大值必然在根节点。pop操作取走根节点(其实是设置底部容器vector的尾端节点)后,为了满足complete binary tree的条件,必须割舍最下层最右边的叶节点,并将其值重新安插至max-heap(因此有必要重新调整heap结构)。
为满足max-heap次序特性,我们执行percolate down下溯程序:将空洞节点和其较大子节点“对调”,并持续下放,直至叶节点为止。然后将前述割舍之元素值设给这个“已到达叶层的空洞节点”,再对它执行一次percolate up上溯程序。
pop_heap算法的实现细节如下,该函数接受两个迭代器,用来表现一个heap底部容器的头尾。如果不符合这个条件,pop_heap的执行结果未可预知。
template
inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last){
__pop_heap_aux(first, last, value_type(first));
}
template
inline void __pop_heap_aux(RandomAccessIterator first, RandomAccessIterator last, T*)
{
__pop_heap(first, last-1, last-1, T(*(last-1)), distance_type(first));
//pop操作的结果应为底部容器的第一个元素,因此,首先设定欲调整值为尾值,然后将首值调整至尾节点,然后重新调整
//[first, last-1),使之重新成一个合格的heap
}
template
inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last, RandomAccessIterator result,
T value, Distance*
){
*result = *first;
//设定尾值为首值,于是尾值即为欲求结果
//可由客户端稍后再以底层容器pop_back()取出尾值
__adjust_heap(first, Distance(0), distance(last-first), value);
//以上欲重新调整heap,洞号为0,欲调整值为value
}
template
void __adjust_heap(RandomAccessIterator first, Distance holeIndex, Distance len, T value)
{
Distance topIndex = holeIndex;
Distance secondChild = 2 * holeIndex + 2;
while(secondChild
注意,pop_heap之后,最大元素只是被置放于底部容器的最尾端,尚未被取走。如果要取其值,可使用底部容器vector所提供的back操作函数。如果要移除它,可使用底部容器vector所提供的pop_back操作函数。