STL算法_heap算法篇

来源:互联网 发布:好用的日记本软件 编辑:程序博客网 时间:2024/05/30 05:42

STL算法_heap算法篇

  • 堆简介

    二叉堆是一种完全二叉树,即整棵二叉树中除了最底层的叶子节点之外,其余节点是填满的,而最底层的叶子节点由左到右也是填满的不能存在空隙。

    堆主要包括两类:大顶堆和小顶堆。大顶堆指每个节点的键值(key)都大于或等于其叶子节点的键值,而小顶堆指每个节点键值都小于等于其节点的键值。STL中堆主要使用vector/array实现的。大顶堆的最大值在根节点,故其总是位于底层的vector/array的起头处;小顶堆的最小值在根节点,故其总是位于vector/array的起头处。

    此算法只要存在于stl_heap.h中,主要有make_heap(),push_heap(),pop_heap(),sort_heap()。

  • make_heap()算法

    此算法用于将一段现有的数据转化为一个heap,即构建二叉堆的过程。

    // 以下这组make_heap()不允许指定“大小比较标准”template <class _RandomAccessIterator, class _Tp, class _Distance>void __make_heap(_RandomAccessIterator __first,            _RandomAccessIterator __last, _Tp*, _Distance*){  if (__last - __first < 2) return;      // 如果长度为0或1,不必重新排列  _Distance __len = __last - __first;  // 找出第一个需要重排的子树头部,以parent标示出。由于任何叶节点都不需执行percolate down,所以  // 有以下计算。parent命名不佳,名为holeIndex更好  _Distance __parent = (__len - 2)/2;  while (true) {    // 重排以parent为首的子树。len是为了让__adjust_heap()判断操作范围    __adjust_heap(__first, __parent, __len, _Tp(*(__first + __parent)));    if (__parent == 0) return;           // 走完根节点,就结束    __parent--;                          // (已重排之子树的)头部向前一个节点  }}// 利用所给的数组建立堆,其主要涉及调整底部vector中元素使其满足堆的条件// 将[first, last)排列为一个heap// 版本1template <class _RandomAccessIterator>inline void make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last){  __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);  __STL_REQUIRES(typename iterator_traits<_RandomAccessIterator>::value_type,                 _LessThanComparable);  __make_heap(__first, __last,              __VALUE_TYPE(__first), __DISTANCE_TYPE(__first));}template <class _RandomAccessIterator, class _Compare,          class _Tp, class _Distance>void__make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,            _Compare __comp, _Tp*, _Distance*){  if (__last - __first < 2) return;  _Distance __len = __last - __first;    // 计算堆的长度  _Distance __parent = (__len - 2)/2;    // 计算根节点位置  while (true) {      // 调整排列元素,使其满足堆    __adjust_heap(__first, __parent, __len, _Tp(*(__first + __parent)),                  __comp);    if (__parent == 0) return;    __parent--;  }}// 将[first, last)排列为一个heap// 版本2template <class _RandomAccessIterator, class _Compare>inline void make_heap(_RandomAccessIterator __first,           _RandomAccessIterator __last, _Compare __comp){  __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);  __make_heap(__first, __last, __comp,              __VALUE_TYPE(__first), __DISTANCE_TYPE(__first));}// 对排序算法template <class _RandomAccessIterator>void sort_heap(_RandomAccessIterator __first, _RandomAccessIterator __last){  __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);  __STL_REQUIRES(typename iterator_traits<_RandomAccessIterator>::value_type,                 _LessThanComparable);  // 以下,每执行一次pop_heap(),极值(在STL heap中为极大值)即被放在尾端。扣除尾端再执行一次pop_heap(),次极值  // 又被放在新尾端。一直下去,最后即得到排序结果  while (__last - __first > 1)    pop_heap(__first, __last--);     // 每执行pop_heap()一次,操作范围即退缩一格}
  • push_heap()算法

    在堆中添加一个元素。为了满足完全二叉树的条件,新加入的元素一定要放在最下一层作为叶子节点,并填补在从左到右的第一个空格出,即将新元素插入在底层vector/array的end()处。插入后,为了满足堆(以大顶堆为例)的条件,还需要执行上溯操作:即将新节点与其父节点比较,如果其键值大于父节点,就将父子对换位置。如此一直上溯,直到不需对换或直到根节点为止。

    // 以下这组push_back()不允许指定“大小比较标准”template <class _RandomAccessIterator, class _Distance, class _Tp>void __push_heap(_RandomAccessIterator __first,            _Distance __holeIndex, _Distance __topIndex, _Tp __value){  _Distance __parent = (__holeIndex - 1) / 2;  while (__holeIndex > __topIndex && *(__first + __parent) < __value) {    // 当尚未达到顶端,且父节点小于新值(于是不符合heap的次序特性)    // 由于以上使用operator<,可知STL heap是一种max-heap(大者为父)    *(__first + __holeIndex) = *(__first + __parent);    // 令洞者为父节点    __holeIndex = __parent;                              // percolate up:调整洞号,向上提升至父节点    __parent = (__holeIndex - 1) / 2;                    // 新洞的父节点  }      *(__first + __holeIndex) = __value;                    // 令洞值为新值,完成插入操作} template <class _RandomAccessIterator, class _Distance, class _Tp>inline void __push_heap_aux(_RandomAccessIterator __first,                _RandomAccessIterator __last, _Distance*, _Tp*){  // 根据implicit represention heap的结构特性:新值必置于底部容器的最尾端,此即第一个洞号:(last-first)-1  __push_heap(__first, _Distance((__last - __first) - 1), _Distance(0),               _Tp(*(__last - 1)));}// push_heap函数,在堆中插入一个新元素// 版本1template <class _RandomAccessIterator>inline void push_heap(_RandomAccessIterator __first, _RandomAccessIterator __last){  __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);  __STL_REQUIRES(typename iterator_traits<_RandomAccessIterator>::value_type,                 _LessThanComparable);  // 注意:此函数被调用时,新元素应已置于底部容器的最尾端  __push_heap_aux(__first, __last,                  __DISTANCE_TYPE(__first), __VALUE_TYPE(__first));}template <class _RandomAccessIterator, class _Distance, class _Tp,           class _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) = *(__first + __parent);    __holeIndex = __parent;    __parent = (__holeIndex - 1) / 2;  }  *(__first + __holeIndex) = __value;}template <class _RandomAccessIterator, class _Compare,          class _Distance, class _Tp>inline void __push_heap_aux(_RandomAccessIterator __first,                _RandomAccessIterator __last, _Compare __comp,                _Distance*, _Tp*) {  __push_heap(__first, _Distance((__last - __first) - 1), _Distance(0),               _Tp(*(__last - 1)), __comp);}// 版本2template <class _RandomAccessIterator, class _Compare>inline void push_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,          _Compare __comp){  __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);  __push_heap_aux(__first, __last, __comp,                  __DISTANCE_TYPE(__first), __VALUE_TYPE(__first));}
  • pop_heap()算法

    在堆中删除根元素。下面以大顶堆为例,取走根节点(实质是设置底部容器vector/array的尾端节点)后,为了满足完全二叉树的条件,必须割舍最下层最右边的叶节点,并将其值重新安插到大顶堆的顶点(即vector/array的首端)。取走根元素后,为了满足大顶堆的条件,还需要执行下溯操作:即将此时的根节点值与其较大的自己子节点比较,如果其子节点更大,则交换,然后持续下方,直到不需对换或达到叶节点为止。

    // __adjust_heap(调整堆)算法:主要用于调整排列中的数据,使其满足堆的性质。此算法主要用在pop_heap()算法中。// 以下这个__adjust_heap()不允许指定“大小比较标准”// 版本1template <class _RandomAccessIterator, class _Distance, class _Tp>void __adjust_heap(_RandomAccessIterator __first, _Distance __holeIndex,              _Distance __len, _Tp __value){  _Distance __topIndex = __holeIndex;  _Distance __secondChild = 2 * __holeIndex + 2;       // 洞节点之右子节点  while (__secondChild < __len) {    // 比较洞节点之左右两个子值,然后以secondChild代表较大的子节点    if (*(__first + __secondChild) < *(__first + (__secondChild - 1)))      __secondChild--;    // percolate down:令较大值为洞值,再令洞号下移至较大子节点处    *(__first + __holeIndex) = *(__first + __secondChild);    __holeIndex = __secondChild;    // 找出新洞节点的右子节点    __secondChild = 2 * (__secondChild + 1);  }  if (__secondChild == __len) {                        // 没有右子节点,只有左子节点    // percolate down:令左子值为洞值,再令洞号下移至左子节点处    *(__first + __holeIndex) = *(__first + (__secondChild - 1));    __holeIndex = __secondChild - 1;  }  // 此时可能尚未满足次序特性。执行一次percolate up操作  __push_heap(__first, __holeIndex, __topIndex, __value);}template <class _RandomAccessIterator, class _Tp, class _Distance>inline void __pop_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,           _RandomAccessIterator __result, _Tp __value, _Distance*){  *__result = *__first;    // 设定尾指为首值,于是尾值即为欲求结果,可由客户端稍后再以底层容器之pop_back()取出尾值  // 以上欲重新调整heap,洞号为0(亦即树根处),欲调整值为value(原尾值)  __adjust_heap(__first, _Distance(0), _Distance(__last - __first), __value);}template <class _RandomAccessIterator, class _Tp>inline void __pop_heap_aux(_RandomAccessIterator __first, _RandomAccessIterator __last,               _Tp*){  // 根据implicit represention heap的次序特性,pop操作的结果应为底部容器的第一个元素。因此,首先  // 设定欲调整值为尾值,然后将首值调至尾节点(所以以上将迭代器result设为last-1),然后重整[first,  // last-1),使之重新成一个合格的heap  __pop_heap(__first, __last - 1, __last - 1,              _Tp(*(__last - 1)), __DISTANCE_TYPE(__first));}// pop_heap算法,从堆中删除元素// 版本1template <class _RandomAccessIterator>inline void pop_heap(_RandomAccessIterator __first,                      _RandomAccessIterator __last){  __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);  __STL_REQUIRES(typename iterator_traits<_RandomAccessIterator>::value_type,                 _LessThanComparable);  __pop_heap_aux(__first, __last, __VALUE_TYPE(__first));}// 调整堆,版本2template <class _RandomAccessIterator, class _Distance,          class _Tp, class _Compare>void__adjust_heap(_RandomAccessIterator __first, _Distance __holeIndex,              _Distance __len, _Tp __value, _Compare __comp){  _Distance __topIndex = __holeIndex;  _Distance __secondChild = 2 * __holeIndex + 2;  while (__secondChild < __len) {    if (__comp(*(__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, __comp);}template <class _RandomAccessIterator, class _Tp, class _Compare,           class _Distance>inline void __pop_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,           _RandomAccessIterator __result, _Tp __value, _Compare __comp,           _Distance*){  *__result = *__first;  __adjust_heap(__first, _Distance(0), _Distance(__last - __first),                 __value, __comp);}template <class _RandomAccessIterator, class _Tp, class _Compare>inline void __pop_heap_aux(_RandomAccessIterator __first,               _RandomAccessIterator __last, _Tp*, _Compare __comp){  __pop_heap(__first, __last - 1, __last - 1, _Tp(*(__last - 1)), __comp,             __DISTANCE_TYPE(__first));}// 版本2template <class _RandomAccessIterator, class _Compare>inline void pop_heap(_RandomAccessIterator __first,         _RandomAccessIterator __last, _Compare __comp){  __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);  __pop_heap_aux(__first, __last, __VALUE_TYPE(__first), __comp);}
  • sort_heap()算法

    因为堆中每次执行pop_heap可获得堆中(大顶堆为例)中键值的最大元素,如果持续对整个heap做pop_heap操作,每次将操作范围从后向前缩减一个元素(pop_heap会把键值最大的元素放在底部容器的最尾端),当整个程序执行完毕时,便可得到递增的序列,即实现利用堆进行排序。

    // 堆排序算法,主要利用pop\_heap()算法实现// 版本1template <class _RandomAccessIterator>void sort_heap(_RandomAccessIterator __first, _RandomAccessIterator __last){  __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);  __STL_REQUIRES(typename iterator_traits<_RandomAccessIterator>::value_type,                 _LessThanComparable);  // 以下,每执行一次pop_heap(),极值(在STL heap中为极大值)即被放在尾端。扣除尾端再执行一次pop_heap(),次极值  // 又被放在新尾端。一直下去,最后即得到排序结果  while (__last - __first > 1)    pop_heap(__first, __last--);     // 每执行pop_heap()一次,操作范围即退缩一格}// 版本2template <class _RandomAccessIterator, class _Compare>void sort_heap(_RandomAccessIterator __first,          _RandomAccessIterator __last, _Compare __comp){  __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);  while (__last - __first > 1)    pop_heap(__first, __last--, __comp);}
  • 参考文献

    STL源码剖析——侯捷

    STL源码

0 0
原创粉丝点击