论C++STL源代码中关于堆算法的那些事
关于堆,我们肯定熟知的就是它排序的时间复杂度在几个排序算法里面算是比較靠上的O(nlogn)常常会拿来和高速排序和归并排序讨论,并且它还有个长处是它的空间复杂度为O(1), 可是STL中没有给我们提供像vector, deque, stack, queue之类的数据结构供我们使用。但在C++STL中却提供了一些列的算法,让我们依然能够使用堆,比方make_heap(), push_heap(), pop_heap(), sort_heap()。今天就来论论这几个算法,在介绍上述算法之前先引入两个关于堆的算法,上述四个算法本质上都使用引入的两个算法。
.png)
          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;
               }
          }
.png)
          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);
              }
          }
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是新插入的元素的值。
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);
}
通过将该算法和介绍的第二个算法相比,可知这个算法就是筛选法。
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。
从pop_heap得之,它每次可获得heap中键值最大的元素。假设持续对整个heap做pop_heap操作,每次将操作范围从后向前缩减一个元素,当整个程序运行完成时,数组就是依照从大到小的顺序递增,以下是实际代码:
template <class RandomAccessIterator>
void sort_heap(RandomAccessIterator first, RandomAccessIterator last) {
while (last - first > 1) pop_heap(first, last--);
}
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 */
另外,我本人开通了微信公众号--分享技术之美,我会不定期的分享一些我学习的东西.
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源代码中关于堆算法的那些事的更多相关文章
- STL源代码分析——STL算法sort排序算法
		前言 因为在前文的<STL算法剖析>中,源代码剖析许多,不方便学习,也不方便以后复习.这里把这些算法进行归类,对他们单独的源代码剖析进行解说.本文介绍的STL算法中的sort排序算法,SG ... 
- STL源代码剖析——STL算法stl_algo.h
		前言 在前面的博文中剖析了STL的数值算法.基本算法和set集合算法.本文剖析STL其它的算法,比如排序算法.合并算法.查找算法等等.在剖析的时候.会针对函数给出一些样例说明函数的使用.源代码出自SG ... 
- STL源代码分析——STL算法remove删除算法
		前言 因为在前文的<STL算法剖析>中,源代码剖析许多.不方便学习,也不方便以后复习,这里把这些算法进行归类.对他们单独的源代码剖析进行解说.本文介绍的STL算法中的remove删除算法. ... 
- STL源代码分析——STL算法merge合并算法
		前言 因为在前文的<STL算法剖析>中.源代码剖析许多.不方便学习.也不方便以后复习,这里把这些算法进行归类.对他们单独的源代码剖析进行解说.本文介绍的STL算法中的merge合并算法. ... 
- STL中的所有算法(70个)
		STL中的所有算法(70个)----9种类型(略有修改by crazyhacking) 参考自: http://www.cppblog.com/mzty/archive/2007/03/14/1981 ... 
- STL源代码剖析——基本算法stl_algobase.h
		前言 在STL中.算法是常常被使用的,算法在整个STL中起到很关键的数据.本节介绍的是一些基本算法,包括equal.fill.fill_n,iter_swap.lexicographical_comp ... 
- STL源代码剖析——STL算法之set集合算法
		前言 本节介绍set集合的相关算法,各自是并集set_union,差集set_difference,交集set_intersection 和对称差集set_symmetric_difference.这 ... 
- STL中的查找算法
		STL中有很多算法,这些算法可以用到一个或多个STL容器(因为STL的一个设计思想是将算法和容器进行分离),也可以用到非容器序列比如数组中.众多算法中,查找算法是应用最为普遍的一类. 单个元素查找 1 ... 
- STL笔记(6)标准库:标准库中的排序算法
		STL笔记(6)标准库:标准库中的排序算法 标准库:标准库中的排序算法The Standard Librarian: Sorting in the Standard Library Matthew A ... 
随机推荐
- 微信小程序开发教程(五)开发框架:MINA
			微信团队为小程序提供的框架命名为MINA应用框架.MINA框架通过封装微信客户端提供的文件系统.网络通信.任务管理.数据安全等基础功能,对上层提供一整套JavaScript API,让开发者能够非常方 ... 
- hibernate中session的线程安全问题
			Hibernate的基本特征是完成面向对象的程序设计语言到关系数据库的映射,在Hibernate中使用持久化对象PO(Persistent Object)完成持久化操作,对PO的操作必须在Sessio ... 
- 使用lookup-method解决singleton bean依赖prototype bean的问题
			在Spring里面,当一个singleton bean依赖一个prototype bean,那么,因为singleton bean是单例的,因此prototype bean在singleton bea ... 
- 【二分答案】【中位数】codeforces 394 bun
			bunDescription因为体育老师很喜欢等差数列,所以他要求学生们站队必须按身高站成等差数列.但是有些班级的学生无论如何也无法排成等差数列,于是体育老师从食堂买来了两种神奇的面包.吃一个第一种面 ... 
- openresty总结
			协程 1.例如当获取的数据没有前后依赖关系时,可以使用ngx.thread.spawn和ngx.thread.wait同时从数据库不同的库.表或者不同来源(mysql,redis等)获取数据. htt ... 
- Spark小问题合集
			1)在win7下使用spark shell运行spark程序,通过以下形式读取文件时 sc.sequenceFile[Int,String]("./sparkF") 偶尔会出现“I ... 
- xcode编译项目Permission denied错误
			打开终端,输入命令 sudo chmod -R 777 工作目录 
- JavaScript 中函数节流和函数去抖的讲解
			JavaScript 中函数节流和函数去抖的讲解 我们都知道频繁触发执行一段js逻辑代码对性能会有很大的影响,尤其是在做一些效果实现方面,或者逻辑中需要进行后端请求,更是会导致卡顿,效果失效等结果,所 ... 
- ActiveMQ安装与持久化消息
			activityMQ官网:http://activemq.apache.org/ 有windows版与linux版 windows版启动 在bin目录下双击activemq.bat linux版的安 ... 
- 通过ssh上传文件到目标主机
			需要通过ssh上传文件到目标主机上,之前一直时通过ssh客户端来传文件的,这次因为本地没装客户端,所以考虑直接用终端通过ssh连接主机进行文件传输. 只需要一条命令就可以了: scp ./serve ... 
