STL中支持堆操作,对外暴露了std::make_heapstd::push_heapstd::pop_heapstd::sort_heapstd::is_heapstd::is_heap_until这6个函数,详细的使用方法可以参见图解STL中算法的分类、简介及其Demo。

这里我简单一下讲一下我对堆这种数据结构的理解,首先,堆是完全二叉树,这意味着堆可以很方便用数组这种数据结构来表示,看下图:

其次,以大顶堆为例子,最大的元素永远在二叉树的根,根到二叉树的叶子的每条路径都是降序的,因此,调整堆的操作就是在这些路径上做插入排序。

声明一下,以下所有源码都来自MinGW中的stl_heap.h,我弄了下代码的格式化和写了点注释。

标准库中的代码写的很好,包括变量的命名,都是很形象的。比如holeIndex,“洞的下标”。

push_heap

/***  @brief  Push an element onto a heap.*  @param  __first  Start of heap.*  @param  __last   End of heap + element.*  @ingroup heap_algorithms**  This operation pushes the element at last-1 onto the valid heap*  over the range [__first,__last-1).  After completion,*  [__first,__last) is a valid heap.*/
template <typename _RandomAccessIterator>
inline void
push_heap(_RandomAccessIterator __first, _RandomAccessIterator __last)
/*
template<typename _RandomAccessIterator, typename _Compare>
inline void
push_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp)
*/
{typedef typename iterator_traits<_RandomAccessIterator>::value_type _ValueType;typedef typename iterator_traits<_RandomAccessIterator>::difference_type _DistanceType;_ValueType __value = std::move(*(__last - 1));std::__push_heap(__first, _DistanceType((__last - __first) - 1),_DistanceType(0), std::move(__value),__gnu_cxx::__ops::__iter_less_val());// __gnu_cxx::__ops::__iter_comp_val(__comp));
}

这里把最后一个元素,也就是新加入堆的元素(最后一个叶子结点)打了个洞,利用std::move,这样,最后一个位置是没有值的,这相当于是一个空洞;

template <typename _RandomAccessIterator, typename _Distance, typename _Tp, typename _Compare>
void
__push_heap(_RandomAccessIterator __first,_Distance __holeIndex, _Distance __topIndex, _Tp __value,_Compare __comp)
{_Distance __parent = (__holeIndex - 1) / 2;while (__holeIndex > __topIndex && __comp(__first + __parent, __value)){*(__first + __holeIndex) = std::move(*(__first + __parent));__holeIndex = __parent;__parent = (__holeIndex - 1) / 2;}*(__first + __holeIndex) = std::move(__value);
}

由于出现了空洞,因此需要元素来填上空洞,而且,新加入进来的元素只会影响一条路径(叶子结点到根节点的路径),因此只要在这条路径上做插入排序就好了,把父亲结点移动到孩子结点直到这条路径上满足堆的性质,这样可以填洞,但是又会把父亲结点弄成洞,所以最后用新加入的元素填住最后的洞,其实就是个向上的插入排序。这样,就把新加入的元素融入了堆。

pop_heap

/***  @brief  Pop an element off a heap.*  @param  __first  Start of heap.*  @param  __last   End of heap.*  @pre    [__first, __last) is a valid, non-empty range.*  @ingroup heap_algorithms**  This operation pops the top of the heap.  The elements __first*  and __last-1 are swapped and [__first,__last-1) is made into a*  heap.*/
template <typename _RandomAccessIterator>
inline void
pop_heap(_RandomAccessIterator __first, _RandomAccessIterator __last)
/*
template<typename _RandomAccessIterator, typename _Compare>
inline void
pop_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp)
*/
{typedef typename iterator_traits<_RandomAccessIterator>::value_type _ValueType;if (__last - __first > 1){--__last;std::__pop_heap(__first, __last, __last,__gnu_cxx::__ops::__iter_less_iter());// __gnu_cxx::__ops::__iter_comp_iter(__comp));}
}

以大顶堆为例,把最大的元素弹出堆,其实就是把根节点也就是数组的第一个元素弄走就行了。

template <typename _RandomAccessIterator, typename _Compare>
inline void
__pop_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,_RandomAccessIterator __result, _Compare __comp)
{typedef typename iterator_traits<_RandomAccessIterator>::value_type _ValueType;typedef typename iterator_traits<_RandomAccessIterator>::difference_type _DistanceType;_ValueType __value = std::move(*__result);*__result = std::move(*__first);std::__adjust_heap(__first, _DistanceType(0),_DistanceType(__last - __first),std::move(__value), __comp);
}

也就是把第一个元素和最后一个元素互换一下,然后把第一个元素的位置打一个洞,之后就调用__adjust_heap来填洞,其实就是个向下的插入排序

下图是__adjust_heap向上提升结点的操作,最后再次调用__push_heap来做插入排序调整堆。

template <typename _RandomAccessIterator, typename _Distance, typename _Tp, typename _Compare>
void __adjust_heap(_RandomAccessIterator __first, _Distance __holeIndex,_Distance __len, _Tp __value, _Compare __comp)
{const _Distance __topIndex = __holeIndex;_Distance __secondChild = __holeIndex;// 1while (__secondChild < (__len - 1) / 2){__secondChild = 2 * (__secondChild + 1);if (__comp(__first + __secondChild, __first + (__secondChild - 1)))__secondChild--;*(__first + __holeIndex) = std::move(*(__first + __secondChild));__holeIndex = __secondChild;}// 2if ((__len & 1) == 0 && __secondChild == (__len - 2) / 2){__secondChild = 2 * (__secondChild + 1);*(__first + __holeIndex) = std::move(*(__first + (__secondChild - 1)));__holeIndex = __secondChild - 1;}// 3std::__push_heap(__first, __holeIndex, __topIndex, std::move(__value), __gnu_cxx::__ops::__iter_comp_val(__comp));
}

make_heap

/***  @brief  Construct a heap over a range.*  @param  __first  Start of heap.*  @param  __last   End of heap.*  @ingroup heap_algorithms**  This operation makes the elements in [__first,__last) into a heap.*/
template <typename _RandomAccessIterator>
inline void
make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last)
/*
template<typename _RandomAccessIterator, typename _Compare>
inline void
make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp)
*/
{std::__make_heap(__first, __last, __gnu_cxx::__ops::__iter_less_iter());// std::__make_heap(__first, __last, __gnu_cxx::__ops::__iter_comp_iter(__comp));
}

叶子结点本来就是一个合格的堆,那么,就要从最后一个非叶子结点向根节点逐渐调整堆,这样可以使得每一个子堆都是合格的堆,那么整体上也就是一个合格的堆了。

template <typename _RandomAccessIterator, typename _Compare>
void
__make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp)
{typedef typename iterator_traits<_RandomAccessIterator>::value_type _ValueType;typedef typename iterator_traits<_RandomAccessIterator>::difference_type _DistanceType;if (__last - __first < 2)return;const _DistanceType __len = __last - __first;_DistanceType __parent = (__len - 2) / 2;while (true){_ValueType __value = std::move(*(__first + __parent));std::__adjust_heap(__first, __parent, __len, std::move(__value), __comp);if (__parent == 0)return;__parent--;}
}

sort_heap

/***  @brief  Sort a heap.*  @param  __first  Start of heap.*  @param  __last   End of heap.*  @ingroup heap_algorithms**  This operation sorts the valid heap in the range [__first,__last).*/
template <typename _RandomAccessIterator>
inline void
sort_heap(_RandomAccessIterator __first, _RandomAccessIterator __last)
/*
template<typename _RandomAccessIterator, typename _Compare>
inline void
sort_heap(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp)
*/
{std::__sort_heap(__first, __last,__gnu_cxx::__ops::__iter_less_iter());// __gnu_cxx::__ops::__iter_comp_iter(__comp));
}

根据堆的性质,堆顶一定是最大或者最小的元素,那么,我们逐渐把堆顶元素和尾部元素互换,然后马上调整堆。这样子,有序的元素会从数组尾部一直扩展到头部,至此,整个数组都是有序的了。

template <typename _RandomAccessIterator, typename _Compare>
void __sort_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,_Compare __comp)
{while (__last - __first > 1){--__last;std::__pop_heap(__first, __last, __last, __comp);}
}

is_heap_until

从根结点找一个最大的合格的堆,如果整个数组是个合格的堆,那么返回数组大小。

/***  @brief  Search the end of a heap.*  @param  __first  Start of range.*  @param  __last   End of range.*  @return  An iterator pointing to the first element not in the heap.*  @ingroup heap_algorithms**  This operation returns the last iterator i in [__first, __last) for which*  the range [__first, i) is a heap.*/
template <typename _RandomAccessIterator>
inline _RandomAccessIterator
is_heap_until(_RandomAccessIterator __first, _RandomAccessIterator __last)
/*
template<typename _RandomAccessIterator, typename _Compare>
inline _RandomAccessIterator
is_heap_until(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp)
*/
{return __first + std::__is_heap_until(__first, std::distance(__first, __last),__gnu_cxx::__ops::__iter_less_iter());// __gnu_cxx::__ops::__iter_comp_iter(__comp));
}

根据堆的性质 – "父亲结点一定大于或者小于两个孩子结点"来依次判断就好了。

template <typename _RandomAccessIterator, typename _Distance, typename _Compare>
_Distance
__is_heap_until(_RandomAccessIterator __first, _Distance __n, _Compare __comp)
{_Distance __parent = 0;for (_Distance __child = 1; __child < __n; ++__child){if (__comp(__first + __parent, __first + __child))return __child;if ((__child & 1) == 0)++__parent;}return __n;
}

is_heap

之前就说了,如果整个数组是个合格的堆,那么__is_heap_until返回数组大小,所以is_heap就是这儿干的。

/***  @brief  Determines whether a range is a heap.*  @param  __first  Start of range.*  @param  __last   End of range.*  @return  True if range is a heap, false otherwise.*  @ingroup heap_algorithms*/
template <typename _RandomAccessIterator>
inline bool
is_heap(_RandomAccessIterator __first, _RandomAccessIterator __last)
{return std::is_heap_until(__first, __last) == __last;// return std::is_heap_until(__first, __last, __comp) == __last;}

聊聊C++11标准库中堆(heap)算法的源码相关推荐

  1. 【C++标准库】std::string用法指南源码剖析

    文章目录 1.ASCII码 (1)计算机如何表达字符 2.C 语言中的字符类型 char (1)思想:char 即整数 (3)C 语言帮手函数 (4)C语言中的字符串 (4)C 语言转义符 3.C++ ...

  2. C++标准库(第二版).pdf与STL源码剖析.pdf下载

    链接:https://pan.baidu.com/s/1KJjkz19AdFd_UHQzBwHd8A 提取码:2191 链接:https://pan.baidu.com/s/1754Oi4BdBE2s ...

  3. 聊聊C++标准库,准标准库中关于时间的概念和用法

    概要 在实际C++业务开发中,经常需要使用系统API或者标准库去获取时间,计算时间的需求,其中,时间按概念又分时间段,时间点:按表达形式又分系统时间,本地时间:其实,获取到了时间,如何通过日志的方式把 ...

  4. 标准库中的智能指针shared_ptr

    智能指针的出现是为了能够更加方便的解决动态内存的管理问题.注:曾经记得有本书上说可以通过vector来实现动态分配的内存的自动管理,但是经过试验,在gcc4.8.5下是不行的.这个是容易理解的,vec ...

  5. C++标准库中的随机数生成

    C++标准库中的随机数生成 一.伪随机与真随机 数字计算机的结果可以说是固定的.必然的.都是根据现有数据的状态得出接下来的状态.除非硬件损坏,计算机不会产生真正的随机和无法预料的事.在生活中随手抛一个 ...

  6. C++11标准库 - array 1

    C++11标准库 - array std::array是原生数组的封装,它存放于栈上且大小固定,性能也与之相同.在原生数组的基础上,它添加了范围检查,以及其它的STL的相应特性,比如复制.交换.迭代器 ...

  7. C++标准库中的数学函数

    C++标准库中的数学函数. 这是一篇我转载的文章,里面有关于数学相关的函数讲解的很详细,供以后自己学习. http://blog.sina.com.cn/s/blog_149e9d2ec0102wxq ...

  8. C++新特性之五:标准库中的正则表达式扩充

    总第34篇 本文主要对C++11/14标准库中的正则表达式的扩充进行详细总结说明,以方便大家在学习工作过程中参考. 1.正则表达式 正则表达式是描述一种字符串匹配的模式.一般使用正则表达式主要实现下面 ...

  9. C++标准库中各种排序归纳

    一.简介 所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作.我们在编程过程中会经常接触到排序,比如游戏中的排行榜等.C++标准库中提供了各种不同的排序算法,这篇博 ...

最新文章

  1. linux(以ubuntu为例)下Android利用ant自动编译、修改配置文件、批量多渠道,打包生成apk文件...
  2. java 密钥工厂 desede_20145212 实验五《Java网络编程》
  3. Python生成html邮件
  4. oracle创建数据库步骤
  5. python 增删列表_python 列表的增删改查
  6. 云计算对于传统软件工程的影响
  7. vcf文件(call variants得来的)怎么看变异是纯合还是杂合的
  8. 学习笔记 | 传统企业互联网改革之道
  9. 20175323 团队项目 服务器端函数功能与业务逻辑详解
  10. 也说说angularJs里的evalAsync
  11. *第二周*数据结构实践项目一【交换】
  12. Linux红帽认证最全介绍
  13. php读取mysql单条数据_用PHP框架与原始代码读取Mysql单条数据性能比较
  14. 词法分析器的java代码_利用Java实现简单的词法分析器实例代码
  15. 超市管理系统软件测试用例图,超市管理系统用户管理模块测试用例集.doc
  16. 【舆情聚焦】 乐视危机舆情监测专项报告
  17. mac:通过鼠标右键 新建文本文档
  18. dimens文件生成器使用方法
  19. 无法使用内置管理员账户打开应用商店
  20. 独家专访丨刘江川:从“边缘”到“中心”,边缘计算科学家的创业之路

热门文章

  1. Character Strings and String Functions——字符串和字符串函数
  2. 学生管理系统(课程设计附带源码)
  3. 卷积神经网络(CNN)基本概念
  4. MySQL中DATEDIFF函数使用
  5. 我们为什么要学习PHP?PHP的应用领域有哪些?
  6. 泰勒公式_高数_1元微积分
  7. Fedora安装mysql(基础安装)
  8. EndNote笔记(一)——导入文献
  9. 项目中XA Transaction应用
  10. python的define_在Python中,使用关键字define定义函数。