关于堆,我们肯定熟知的就是它排序的时间复杂度在几个排序算法里面算是比較靠上的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. Linux内核ROP姿势详解(二)

    /* 很棒的文章,在freebuf上发现了这篇文章上部分的翻译,但作者貌似弃坑了,顺手把下半部分也翻译了,原文见文尾链接 --by JDchen */ 介绍 在文章第一部分,我们演示了如何找到有用的R ...

  2. CentOS5.5下直接安装MySQL5.6

    1.安装平台:CentOS5.5 2.由于从MySQL5.0升到MySQL5.6出现重大问题,为了不影响进度,暂时先直接安装MySQL5.6 2.1首先确认yum源,这里使用http://repo.m ...

  3. MongoError: topology was destroyed解决方法

    MongoError: topology was destroyed 分析得出,出现这个问题是因为,当mongodb尝试写入某个数据的时候,连接被中断了! 解决方法:检查代码中是否存在操作数据的过程中 ...

  4. [BZOJ1790][AHOI2008]Rectangle 矩形藏宝地(四维偏序,CDQ+线段树)

    1790: [Ahoi2008]Rectangle 矩形藏宝地 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 553  Solved: 193[Subm ...

  5. Xshell连接VM中Ubuntu

    摘要:终端输入ifconfig获取本地虚拟机的IP地址;安装openssh-serversudoapt-getinstallopenssh-server 查看server是否启动: ps-ef|gre ...

  6. GAILS里面的SAVE方法

    用途 保存一个domain类的实例到数据库,需要的话会级联保存所有的子实例. 举例 def b = new Book(title:"The Shining") b.save() 描 ...

  7. 在Delphi中使用键盘勾子获取键盘输入(译--5月7日)

    http://blog.sina.com.cn/s/blog_502b2e970100949s.html 获取键盘输入以控制无法接受输入焦点的控件考虑一些游戏,显示图片在TPainBox,但是TPai ...

  8. oracle 百万行数据优化查询

      1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使 ...

  9. 使用x64dbg分析微信聊天函数并实现发信息

    1.引言 我们知道微信现在不光在手机上很常用,在电脑也是非常常用的,尤其是使用微信联系客户和维护群的人,那这个时候每天都会定期发送一些信息,如果人工操作会很累,所以自动化工具是王道,本节就使用x64d ...

  10. [Python爬虫] 之十八:Selenium +phantomjs 利用 pyquery抓取电视之家网数据

    一.介绍 本例子用Selenium +phantomjs爬取电视之家(http://www.tvhome.com/news/)的资讯信息,输入给定关键字抓取资讯信息. 给定关键字:数字:融合:电视 抓 ...