优先队列(priority queue)是由二叉堆(binary heap)实现的,它是一种完全二叉树(complete binary tree)。也就是说,整棵二叉树binary tree除了最底层的叶节点之外,是填满的,而最底层的叶节点由左至右又不得有空隙。
完全二叉树的这个特点,给我们实现二叉堆带来了极大的便利:
以最大堆max-heap为例,介绍下插入与出堆操作
插入操作过程大概描述就是:
插入操作STL源码如下:
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
}
// 以下这组push_back()不允许指定“大小比较标准”
template
void __push_heap(RandomAccessIterator first, Distance holeIndex, Distance topIndex, T value)
{
Distance parent = (holeIndex - 1) / 2; //找出父节点
while (holeIndex > topIndex && *(first + parent) < value)
{
// 当尚未到达顶端,且父节点小于新值(于是不符合heap的次序特性)
// 由于以上使用operator<,可知STL heap是一种max-heap(大者为父)
*(first +holeIndex) = *(first + parent); // 令洞值为父值
holeIndex = parent; // percolate up:调整洞号,向上提升至父节点
parent = (holeIndex -1) / 2; // 新洞的父节点
} // 持续至顶端,或满足heap的次序特性为止
*(first + holeIndex) = value; // 令洞值为新值,完成插入操作
}
出堆操作:
出堆STL源码如下:
template
inline coid 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));
// 以上,根据 implicit representation heap 的次序特性,pop操作的结果
// 应为底部容器的第一个元素。因此,首先设定欲调整值为尾值,
// 然后将首值调至尾节点(所以以上将迭代器result设为last-1)。
// 然后重整[first, last-1),使之重新称为一个合格的heap
}
// 以下这组 __pop_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);
// value为原尾值,原尾值放到根节点,并重新调整heap
}
// 以下这个__adjust_heap() 不允许指定“大小比较标准”
template
void __adjust_heap(RandomAccessIterator first, Distance holeIndex, Distance len, T value)
{
Distance topIndex = holeIndex;
Distance secondChild = 2 * holeIndex + 2; // 洞节点之右子节点
while (secondChild < len)
{
// 比较洞节点之左右两个子值,然后以secondChild代表较大子节点
if (*(first + secondChild) < *(first + (secondChild - 1)))
secondChild--;
// 令较大子值为洞值,再令洞号下移至较大子节点处
*(first + holeIndex) = *(first + secondChild);
holeIndex = secondChild;
// 找出新洞节点的右子节点
secondChild = 2 * (secondChild + 1);
}
if (secondChild == len) // 没有右子节点,只有左子节点
{
// Percolate down:令左子节点为洞值,再令洞号下移至左子节点处
*(first + holeIndex) = *(first + (secondChild - 1));
holeIndex = secondChild - 1;
}
// 再执行一次percolate up操作以保证满足次序特性
__push_heap(first, holeIndex, topIndex, value);
}
可以看出,在堆中的元素插入与极值的取得就有O(logN)的表现
STL中的优先队列priority queue的底层就是由heap实现的