Heap堆是常用的数据结构,Heap中也可以存放元素。但是STL中并没有提供Heap容器,只是提供了关于Heap操作的算法。只要支持RandomAccessIterator的容器都可以作为Heap容器。Heap的背景是建立在优先队列的基础上,priority queue允许我们任意的插入一个元素,但是会自动将元素进行排序,使得取出时一定是从优先级最高的元素开始取。我们可以考虑使用list这种数据结构完成priority queue,也就是说,我们可以轻松的完成插入(因为list其实是线性链表),但是我们要取得list中的极值时,则需要对整个list进行遍历。或者我们可以这样考虑,将元素插到list的合适位置,这样一来,虽然很容易取得极值,但是我们的搜索插入也是需要线性搜索。
很自然的想到二分法来减少复杂度,也就是使用二叉搜索树,这样插入和求极值都值需要O(logN)的复杂度,但是这要求二叉搜索树的输入元素要随机,不能按照有序的输入,而且二叉搜索树不容易实现,所以我们选择binary heap来构造这样的容器【方便进行插入,检索,排序】
Binary heap其实就是一棵完全二叉树,我们知道完全二叉树可以使用普通的array来实现,因为节点i的左子节点就在2i处,右子节点在2i+1处,而父节点则在i/2处,这种表述法称为隐式表述法,考虑到array无法动态改变大小,我们使用之前定义的vector来实现我们的heap.
当向heap中插入元素时,插入到末尾,“向上维护”即可:指的是把插入结点与其父结点比较,如果不符合堆得要求则交换,再向上维护其父结点……
当在heap取出元素时,把末尾元素放到Heap头,"向下维护“即可:指的是父结点与孩子结点比较,如果不满足要求,与较大(较小)一个交换,再维护交换的孩子结点……
当在heap取出元素时,把末尾元素放到Heap头,"向下维护“即可:指的是父结点与孩子结点比较,如果不满足要求,与较大(较小)一个交换,再维护交换的孩子结点……
Heap不允许遍历其结点,所以Heap没有迭代器。
push_heap 算法
为了满足complete binary tree条件,新加入的元素一定要放在最下一层作为叶子节点,并填在由左至右的第一个空格,也就是把新元素插入到底层vector的end()处。为满足max-heap的条件(每个节点的键值都大于等于其子节点键值),我们执行percolate up(上溯)程序:将新节点拿来与父节点比较,如果键值(key)比父节点大,就父子对换位置。
![http://www.xpc-yx.com/wp-content/uploads/2015/01/push_heap.png](http://www.xpc-yx.com/wp-content/uploads/2015/01/push_heap.png)
template<calss RandomAccessIterator>
inline void upSortLastElem( RandomAccessIterator first, RandomAccessIterator last)
{
__push_heap_aux(first,last,distance_type(first),value_type(first));
}
//向上维护
template <class RandomAccessIterator, class Distance, class T>
void __push_heap(RandomAccessIterator first, Distance holeIndex,
Distance topIndex, T value) {
Distance parent = (holeIndex - 1) / 2; // 找出父结点
//父结点不是heap顶 且 父节点的值小于孩子结点(小于号<可以看出STL维护的是max heap)
while (holeIndex > topIndex && *(first + parent) < value) {
*(first + holeIndex) = *(first + parent); // 父结点下调
holeIndex = parent; // 维护父结点
parent = (holeIndex - 1) / 2;
}
//已达到heap顶或者满足heap性质后,给新插入元素找到合适位置
*(first + holeIndex) = value;
}
template <class RandomAccessIterator, class Distance, class T>
inline void __push_heap_aux(RandomAccessIterator first,
RandomAccessIterator last, Distance*, T*) {
__push_heap(first, Distance((last - first) - 1), Distance(0),
T(*(last - 1)));
}
//往heap添加元素。注意:此函数调用时,元素已经添加到末尾了,且迭代器已经加1了
template <class RandomAccessIterator>
inline void push_heap(RandomAccessIterator first, RandomAccessIterator last) {
// 注意,此函式被呼叫時,新元素應已置於底層容器的最尾端。
__push_heap_aux(first, last, distance_type(first), value_type(first));
}
//向上维护,允许用自己定义的comp函数,这是未必是max heap了
template <class RandomAccessIterator, class Distance, class T, class Compare>
void __push_heap(RandomAccessIterator first, Distance holeIndex,
Distance topIndex, T value, Compare comp) {
Distance parent = (holeIndex - 1) / 2;//找到父结点的位置
while (holeIndex > topIndex && comp(*(first + parent), value)) {
*(first + holeIndex) = *(first + parent);
holeIndex = parent;
parent = (holeIndex - 1) / 2;
}
*(first + holeIndex) = value;
}
pop_heap算法 执行下溯程序,
template <class RandomAccessIterator, class T>
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后的结果是容器的第一个元素。
把最后一个元素放到到容器头,因此维护时维护区间是[first last-1)。
*/
}
//取出heap顶元素,按照max heap属性来维护heap
template <class RandomAccessIterator>
inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last) {
__pop_heap_aux(first, last, value_type(first));
}
//向下维护heap,允许执行比较函数comp
template <class RandomAccessIterator, class Distance, class T, class Compare>
void __adjust_heap(RandomAccessIterator first, Distance holeIndex,
Distance len, T value, Compare comp) {
Distance topIndex = holeIndex;
Distance secondChild = 2 * holeIndex + 2;
while (secondChild < len) {
if (comp(*(first + secondChild), *(first + (secondChild - 1))))
secondChild--;
*(first + holeIndex) = *(first + secondChild);
holeIndex = secondChild;
secondChild = 2 * (secondChild + 1);
}
if (secondChild == len) {
*(first + holeIndex) = *(first + (secondChild - 1));
holeIndex = secondChild - 1;
}
__push_heap(first, holeIndex, topIndex, value, comp);
}
sort_heap 算法
template< class RandomAccessIterator>
void sort_heap(RandomAccessIterator first, RandomAccessIterator last)
{
<span style="white-space:pre"> </span>while ( last-first>1) //while 判断的边界
<span style="white-space:pre"> </span>//堆排序只需要不断的进行pop,就可以得到有序堆
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>Pop_heap(first, last--); //不断调用pop_heap来达到效果,因为pop过程中并不是把元素删了,而是把最大的移到最后
<span style="white-space:pre"> </span>last--;
<span style="white-space:pre"> </span>}
}
make_heap算法
template<class RandomAccessIterator>
inline void make_heap( RandomAccessIterator first , RandomAccessIterator last)
{
<span style="white-space:pre"> </span>__make_heap( first, last,value_type(first), distance_type(first));
}
template<class RandomAccessIterator,class T, class Distance>
void __make_heap(RandomAccessIterator first, RandomAccessIterator last, T*,Distance*)
{
<span style="white-space:pre"> </span>if (last – first <2 ) return;
<span style="white-space:pre"> </span>Distance len = last – first;
<span style="white-space:pre"> </span>Distance parent = (len-2)/2; //只能对树的内部节点进行“下溯”
<span style="white-space:pre"> </span>while(true)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span> __adjust_heap( first, parent, len,T(*(first+parent)));
<span style="white-space:pre"> </span> If(parent==0) return;
<span style="white-space:pre"> </span> --parent;
<span style="white-space:pre"> </span>}
}