关于堆,我们肯定熟知的就是它排序的时间复杂度在几个排序算法里面算是比較靠上的O(nlogn)常常会拿来和高速排序和归并排序讨论,并且它还有个长处是它的空间复杂度为O(1), 可是STL中没有给我们提供像vector, deque, stack, queue之类的数据结构供我们使用。但在C++STL中却提供了一些列的算法,让我们依然能够使用堆,比方make_heap(), push_heap(), pop_heap(), sort_heap()。今天就来论论这几个算法,在介绍上述算法之前先引入两个关于堆的算法,上述四个算法本质上都使用引入的两个算法。

一、关于堆的两个算法
     1. 堆的插入算法
          以下先看个样例,一个数组a[]={1,9,5,13}。建一个最大堆,下图为过程:
          
          从图中能够看出没插入一个元素,调整后都是一个最大堆。并且是插入到最后一个上(如果数组是能够动态增长的),这样是从下往上进行调整(percolate up)。这种调整算法叫插入法, 以下来看看插入法的代码实现:
          void insert_heap(int a[], int i){
while(i>0){
int j=(i-1)/2;
if(a[i] < a[j]){
swap(a[i], a[j]);
i=j;
}
else break;
}
}

     2. 堆的筛选法
          我们还是来先看两个用堆排序的样例
          例:a[]={1, 9, 5, 13}, 排序步骤例如以下图
          
          像这样一次自上而下的调整过程。叫做筛选法, 算法实现:
          void sift_heap(int a[], int i){
// j 是 i 的左孩子
int j=2*i+1;
int n = strlen(a);
while(j<n){
if(j<n-1 && a[j]<a[j+1]) ++j;
if(a[i]<a[j]){
swap(a[i],a[j]);
i=j;
j=2*i+1;
}
else
break;
}
}
void build_heap(int a[], int n){
int i=n/2;
//循环完后,就建立了一个最大堆
for(; i>=0; ++i){
sift_heap(a, i);
}
}

二、STL算法中的xxxx_heap算法
     stl中的关于堆的算法都是在vector上实现的,而且实现的最大堆,且最大的元素位于数组的第一个位置。

     1. push_heap
          首先来看看STL中的该算法的实现,该算法实如今stl_heap.h文件里,再此函数被调用的时候。新元素已经插入究竟部容器的最尾端。
          template <class RandomAccessIterator, class Compare>
inline void push_heap(RandomAccessIterator first, RandomAccessIterator last){
__push_heap_aux(first, last, distance_type(first), value_type(first));
}
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)));
}
template <class RandomAccessIterator, class Distance, class T>
void __push_heap(RandomAccessIterator first, Distance holeIndex, Distance topIndex, T value) {
Distance parent = (holeIndex - 1) / 2;
while (holeIndex > topIndex && *(first + parent) < value) {
*(first + holeIndex) = *(first + parent);
holeIndex = parent;
parent = (holeIndex - 1) / 2;
}
*(first + holeIndex) = value;
}

能够看到push_heap算法终于调用的是__push_heap,算法内部都用的是迭代器,first是指向堆顶的迭代器,holeIndex指的新插入元素位置距离堆顶的距离。first+topIndex是该堆顶的位置,topIndex是距离, value是新插入的元素的值。

          能够看出__push_heap就是我们介绍的第一种堆算法----插入法。


     2. __adjust_heap
          在具体介绍pop_heap, sort_heap, make_heap三个算法前先来介绍还有一个算法__adjust_heap,__adjust_heap函数定义例如以下:
          template <class RandomAccessIterator, class Distance, class T>
void __adjust_heap(RandomAccessIterator first, Distance holeIndex, Distance len, T value) {
Distance topIndex = holeIndex;
Distance secondChild = 2 * holeIndex + 2; //得到右孩子节点
while (secondChild < len) {
if (*(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);
}

通过将该算法和介绍的第二个算法相比,可知这个算法就是筛选法。


     3. pop_heap
          pop_heap算法并非将堆顶元素删除,而仅仅是将其移到堆的最后。然后调整堆的时候依照len=len-1, 来进行调整,调整后之前最大的元素放在了容器的最后。能够用back()得到。
          函数定义例如以下
          template <class RandomAccessIterator>
inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last) {
__pop_heap_aux(first, last, value_type(first));
}
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));
}
template <class RandomAccessIterator, class T, class Distance>
inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last, RandomAccessIterator result, T value, Distance*) {
*result = *first;
__adjust_heap(first, Distance(0), Distance(last - first), value);
}

能够看出pop_heap最后调用的是__adjust_heap。


     4. sort_heap
          sort_heap就是对堆进行排序。

从pop_heap得之,它每次可获得heap中键值最大的元素。假设持续对整个heap做pop_heap操作,每次将操作范围从后向前缩减一个元素,当整个程序运行完成时,数组就是依照从大到小的顺序递增,以下是实际代码:

          template <class RandomAccessIterator>
void sort_heap(RandomAccessIterator first, RandomAccessIterator last) {
while (last - first > 1) pop_heap(first, last--);
}

     5. make_heap

          make_heap用来将一段现有的数据转化为一个heap。以下是详细实现:
          template <class RandomAccessIterator, class Compare>
inline void make_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp) {
__make_heap(first, last, comp, value_type(first), distance_type(first));
}
template <class RandomAccessIterator, class Compare, class T, class Distance>
void __make_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp, T*, Distance*) {
if (last - first < 2) return; // 长度为0或1。就返回;
Distance len = last - first;
//找到第一个调整的节点
Distance parent = (len - 2)/2;
while (true) {
__adjust_heap(first, parent, len, T(*(first + parent)), comp);
if (parent == 0) return;
parent--;
}
}

能够看见。该算法是从len/2開始调整,一直到parent为0,这就是我在上面第一部分的筛选法时候举的样例。

注: 以上所列出的STL算法均为SGI STL而且,列出的均为不能仅仅能排序规则的一组。

三、STL中堆的应用

尽管stl中没有提供堆这个数据结构,可是priority queue的内部确是由堆来实现的。priority queue同意用户以不论什么次序将不论什么元素推入容器内,但取出的时候一定是从优先权最高的元素開始取。

binary heap正好具有这种特性。为了平衡各个操作的时间复杂度和实现的复杂度,binary heap适合作为priority queue的底层机制。

以下贴一个priority_queue的完整实现代码。请注意在几个构造函数中都使用了make_heap算法。在push函数中使用了push_heap算法。在pop中使用了pop_heap算法:

#ifndef __STL_LIMITED_DEFAULT_TEMPLATES
template <class T, class Sequence = vector<T>,
class Compare = less<typename Sequence::value_type> >
#else
template <class T, class Sequence, class Compare>
#endif
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) {} #ifdef __STL_MEMBER_TEMPLATES
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last, const Compare& x)
: c(first, last), comp(x) { make_heap(c.begin(), c.end(), comp); }
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last)
: c(first, last) { make_heap(c.begin(), c.end(), comp); }
#else /* __STL_MEMBER_TEMPLATES */
priority_queue(const value_type* first, const value_type* last,
const Compare& x) : c(first, last), comp(x) {
make_heap(c.begin(), c.end(), comp);
}
priority_queue(const value_type* first, const value_type* last)
: c(first, last) { make_heap(c.begin(), c.end(), comp); }
#endif /* __STL_MEMBER_TEMPLATES */ 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 {
c.push_back(x);
push_heap(c.begin(), c.end(), comp);
}
__STL_UNWIND(c.clear());
}
void pop() {
__STL_TRY {
pop_heap(c.begin(), c.end(), comp);
c.pop_back();
}
__STL_UNWIND(c.clear());
}
}; // no equality is provided __STL_END_NAMESPACE #endif /* __SGI_STL_INTERNAL_QUEUE_H */
假设你认为本篇对你有收获,请帮顶。

另外,我本人开通了微信公众号--分享技术之美,我会不定期的分享一些我学习的东西.

你能够搜索公众号:swalge 或者扫描下方二维码关注我

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc3dhZ2xl/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

(转载文章请注明出处: http://blog.csdn.net/swagle/article/details/24330605 )

论C++STL源代码中关于堆算法的那些事的更多相关文章

  1. STL源代码分析——STL算法sort排序算法

    前言 因为在前文的<STL算法剖析>中,源代码剖析许多,不方便学习,也不方便以后复习.这里把这些算法进行归类,对他们单独的源代码剖析进行解说.本文介绍的STL算法中的sort排序算法,SG ...

  2. STL源代码剖析——STL算法stl_algo.h

    前言 在前面的博文中剖析了STL的数值算法.基本算法和set集合算法.本文剖析STL其它的算法,比如排序算法.合并算法.查找算法等等.在剖析的时候.会针对函数给出一些样例说明函数的使用.源代码出自SG ...

  3. STL源代码分析——STL算法remove删除算法

    前言 因为在前文的<STL算法剖析>中,源代码剖析许多.不方便学习,也不方便以后复习,这里把这些算法进行归类.对他们单独的源代码剖析进行解说.本文介绍的STL算法中的remove删除算法. ...

  4. STL源代码分析——STL算法merge合并算法

    前言 因为在前文的<STL算法剖析>中.源代码剖析许多.不方便学习.也不方便以后复习,这里把这些算法进行归类.对他们单独的源代码剖析进行解说.本文介绍的STL算法中的merge合并算法. ...

  5. STL中的所有算法(70个)

    STL中的所有算法(70个)----9种类型(略有修改by crazyhacking) 参考自: http://www.cppblog.com/mzty/archive/2007/03/14/1981 ...

  6. STL源代码剖析——基本算法stl_algobase.h

    前言 在STL中.算法是常常被使用的,算法在整个STL中起到很关键的数据.本节介绍的是一些基本算法,包括equal.fill.fill_n,iter_swap.lexicographical_comp ...

  7. STL源代码剖析——STL算法之set集合算法

    前言 本节介绍set集合的相关算法,各自是并集set_union,差集set_difference,交集set_intersection 和对称差集set_symmetric_difference.这 ...

  8. STL中的查找算法

    STL中有很多算法,这些算法可以用到一个或多个STL容器(因为STL的一个设计思想是将算法和容器进行分离),也可以用到非容器序列比如数组中.众多算法中,查找算法是应用最为普遍的一类. 单个元素查找 1 ...

  9. STL笔记(6)标准库:标准库中的排序算法

    STL笔记(6)标准库:标准库中的排序算法 标准库:标准库中的排序算法The Standard Librarian: Sorting in the Standard Library Matthew A ...

随机推荐

  1. 主席树 STL+二分【p3939】数颜色

    Description 小 C 的兔子不是雪白的,而是五彩缤纷的.每只兔子都有一种颜色,不同的兔子可能有 相同的颜色.小 C 把她标号从 \(1\) 到 \(n\) 的 \(n\) 只兔子排成长长的一 ...

  2. [CF396E]On Iteration of One Well-Known Function

    题意:给定$n=\prod\limits_{i=1}^mp_i^{a_i}$,求$\varphi\left(\cdots\varphi\left(n\right)\cdots\right)$(有$k$ ...

  3. Java学习笔记(4)

    控制流程语句之---------switch选择判断语句 switch(你的选择) case 值1: 符合条件执行的语句: break: case 值2: 符合条件执行的语句: break: case ...

  4. IO 流(File)

    1.创建文件 package com.ic.demo01; import java.io.File; import java.io.IOException; public class FileDemo ...

  5. iOS开发——检测屏幕旋转

    步骤一.注册通知 1: [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarOrien ...

  6. Cisco模拟器Web-IOU使用说明 转

    http://blog.sina.com.cn/s/blog_af0abf1f0102uztk.html   GNS3作为使用最多的Cisco官方模拟器,是因为它使用简单,所有设置图形化,是一款非常强 ...

  7. mormot 数据集和JSON互相转换

    mormot 数据集和JSON互相转换 uses SynVirtualDataSet, mORMotMidasVCL, SynCommons; procedure TForm1.Button1Clic ...

  8. 微信跳一跳 可以直接更改分数, POST 请求没有校验

    这两天逛 v 站出现了一众微信跳一跳 'AI',已经被刷屏了…… https://www.v2ex.com/t/418833 https://www.v2ex.com/t/418775 https:/ ...

  9. Python中调用其他程序的方式

    前言 在Python中,可以方便地使用os模块来运行其他脚本或者程序,这样就可以在脚本中直接使用其他脚本或程序提供的功能,而不必再次编写实现该功能的代码.为了更好地控制运行的进程, 可以使用win32 ...

  10. EffectiveJava(24)使用@SuppressWarnings("unchecked")消除非受检警告

    -..使用泛型编程时,会遇到许多编译器警告,如:非受检强制转化警告,非受检方法调用警告,非受检普通数组创建警告,费受精转换警告.这次的内容就是遇到这些警告的时候你该怎么办. PS:非受检警告就是代码上 ...