论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 ...
随机推荐
- [COGS2479 && COGS2639]高维偏序(CDQ分治,bitset)
COGS2479:四维偏序. CDQ套CDQ CDQ:对a分治,对b排序,再对a打标记,然后执行CDQ2 CDQ2:对b分治,对c归并排序,对d树状数组. #include<cstdio> ...
- 北京DAY1下午
省选模拟题 周子凯 题目概况 中文题目名 简易比特币 计算 路径 英文题目名 bit calculation Path 输入文件名 bit.in calculation.in path.in 输出文件 ...
- matlab和c混合编程
参考: http://blog.sciencenet.cn/blog-620659-579885.html http://baike.baidu.com/link?url=HnHd3lU9mcVXwh ...
- vscode 使用笔记
https://code.visualstudio.com/docs/setup/setup-overview#_proxy-server-support 如果使用代理上网时,需要配置: 在 se ...
- coco2dx jni 调用 java 相机返回 图片数据
新建 一个项目 名字:testJin 包名:com.TanSon.org python命令:python create_project.py -project testJin -package c ...
- Protostuff序列化工具类
源代码 package org.wit.ff.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStre ...
- springBoot框架的一些概念
Spring的发展史1. Spring1.x 时代 在Spring1.x时代,都是通过xml文件配置bean,随着项目的不断扩大,需要将xml配置分放到不同的配置文件中,需要频繁的在java类和xml ...
- 加入新的linux系统调用
上一篇详解了linux系统调用的原理,接下来依据上一篇的原理简介怎样创建新的linux系统调用 向内核中加入新的系统调用,须要运行3个步骤: 1. 加入新的内核函数 2. 更新头文件unistd.h ...
- iOS:自定义代码块{ }
1.Xcode本身带有编码常用的代码块可供使用,如下图 调用方法: (1)直接拖到代码区域中: (2)使用快捷键,键入 “while”, Xcode就会出现自动完成的提示 也可以自定义自己常用的代码块 ...
- php之变量覆盖漏洞讲解
1.变量没有初始化的问题(1): wooyun连接1:[link href="WooYun: PHPCMS V9 member表内容随意修改漏洞"]tenzy[/link] $up ...