版权声明:本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名elloop(包含链接)
本系列文章的目录在这里:目录. 通过目录里可以对STL总体有个大概了解
前言
本文介绍如何使用STL里的heap(堆)算法。第一次接触heap这种数据结构是在大学的数据结构教材上,它是一棵完全二叉树。在STL中,heap是算法的形式提供给我们使用的。包括下面几个函数:
-
make_heap
: 根据指定的迭代器区间以及一个可选的比较函数,来创建一个heap. O(N)
-
push_heap
: 把指定区间的最后一个元素插入到heap中. O(logN)
-
pop_heap
: 弹出heap顶元素, 将其放置于区间末尾. O(logN)
-
sort_heap
:堆排序算法,通常通过反复调用pop_heap
来实现. N*O(logN)
C++11加入了两个新成员:
因为heap以算法的形式提供,所以要使用这几个api需要包含 #include <algorithm>
heap相关算法的使用
make_heap
STL中的通过make_heap
创建的堆,默认是大顶堆(max heap),即每个非叶子节点元素的值均不”小于”(默认使用<作为比较准则)其左右孩子节点。要改变堆的建立准则,可以自己制定一个比较函数,如下第二个版本的make_heap
声明:
示例代码:
默认的make_heap
需要注意的是,make_heap
需使用随机迭代器来创建heap。
自己指定比较函数的make_heap
输出:
这里使用了greater<int>()
来代替默认的less<int>()
来创建int类型的heap。可以按层次遍历的顺序把这个heap画出来,可以看到它跟默认情况刚好相反,会是一个小顶堆。
push_heap
与make_heap
类似,push_heap
也有两个版本,其中有一个版本可以指定堆的比较函数,并且也是以一对迭代器指定的区间来进行操作。
示例代码:
先用make_heap
来构造一个堆,然后在容器末尾追加元素之后,把新的迭代器区间传给push_heap
,这样新尾部元素也被添加到堆中。
注意:使用push_heap(f, l)
的话,调用者需要确保[f, l-1)
已经是一个堆. push_heap(f, l)
仅仅会把*(l-1)
插入到[f, l-1)
这个区间形成的堆中,时间复杂度是O(logN).
即, STL书中所述:the caller has to ensure, on entry, the elements in the range [begin, end)
are a heap(according to the same sorting criterion).
如果一开始不用make_heap
处理,直接push_heap
会怎样?
输出:
可以看出直接调用push_heap
的结果并不是一个heap. 下面要提到的pop_heap
也有同样的要求。
pop_heap
它的作用是:交换*first
和*(last-1)
, 然后把[first, last-1)
建成一个max heap. 也就是说把堆顶的最大元素交换到区间尾,然后把除了尾部的元素的剩余区间重新调整成堆。
需要注意的是,调用者要保证,在调用pop_heap
时[first, last)
已经是一个堆(使用相同的排序准则)。
输出:
sort_heap
sort_heap
即经典的堆排序算法,通过每次弹出堆顶直到堆为空,依次被弹出的元素就组成了有序的序列了。STL中的priority_queue
即使用heap的这个特性来实现。
使用sort_heap(f, l)
处理过的区间因为已经有序,就不再是一个heap了。
输出:
注意:调用者仍需确保区间已经是一个堆。
is_heap
示例:
输出:
is_heap_until
原型:
示例:
总结
-
建堆,make_heap
-
堆操作:增加元素(push_heap
),删除元素(pop_heap
), 排序(sort_heap
), 均要求区间已经是一个heap,并且是与当前操作使用相同的排序准则
-
is_heap
, is_heap_until
当做辅助判断函数来用
-
所有的heap算法操作的区间都需要是随机迭代器组成
源码及参考链接
在这里也能看到这篇文章:github博客, CSDN博客, 欢迎访问