序列容器之 heap

来源:互联网 发布:行程编码压缩算法 编辑:程序博客网 时间:2024/06/07 05:03

序列容器之 heap

heap并不归属于STL容器组件,它是个幕后英雄,扮演priority queue ( 4.8节)的助手。顾名思义,priority queue允许用户以任何次序将任何元素推入容器内,但取出时一定是从优先权最高(也就是数值最高)的元素开始取。binary max heap正是具有这样的特性,适合作为priority queue的底层机制。

因为优先队列是每次取优先级最高的元素。如果低层以list实现,每次需要sort().显然不现实。而binary search tree则显得太严格,"太重"。而binary heap是个比较合适的选择。

二叉堆就是一种完全二叉树。也就是说除了叶子节点以外,是填满的。并且每个节点都大于或等于子节点的值。

因为这个性质,我们可以使用vector来存储数据。

heap算法

push_heap

把元素放入堆中。首先新放入的元素是在vector的最末尾,然而插入之后未必满足堆的性质。最大堆中要求,根节点必须的最大的节点。所以尾节点和父节点进行比较,交换,以达到目的。

template <class _RandomAccessIterator>inline void push_heap(_RandomAccessIterator __first, _RandomAccessIterator __last){//__DISTANCE_TYPE 类型转换  __push_heap_aux(__first, __last,                  __DISTANCE_TYPE(__first), __VALUE_TYPE(__first));}template <class _RandomAccessIterator, class _Distance, class _Tp>inline void __push_heap_aux(_RandomAccessIterator __first,                _RandomAccessIterator __last, _Distance*, _Tp*){  __push_heap(__first, _Distance((__last - __first) - 1), _Distance(0),               _Tp(*(__last - 1)));}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;//把值插入到当前当前位置}

pop_heap

pop算法是取最大的元素。取出的时候破坏两点:

  1. 元素的个数-1。

  2. 由哪个节点去顶替根节点。

针对第二点,我们从根节点进行下溯,找到最大的节点。

因为当前节点一定大于子节点。所以取较大的节点顶替取出的根节点,并且下溯到根为止。

针对第一点,我们把原本的最后一个元素放入目前根中的空节点,做一遍push_heap。

元素数量减一。完成工作。

template <class _RandomAccessIterator, class _Compare>inline void pop_heap(_RandomAccessIterator __first,         _RandomAccessIterator __last, _Compare __comp){    __pop_heap_aux(__first, __last, __VALUE_TYPE(__first), __comp);}template <class _RandomAccessIterator, class _Tp, class _Compare>inline void __pop_heap_aux(_RandomAccessIterator __first,               _RandomAccessIterator __last, _Tp*, _Compare __comp){  //以上,根据iplicit representation heap的次序特性,pop操作的结果//应为底部容器的第一个元素。因此,首先设定欲调整值为尾值,然后将首值调至//尾节点(所以以上将迭代器result设为last-1)。然后重整[first, last ),//使之重新成一个合格的heap  __pop_heap(__first, __last - 1, __last - 1, _Tp(*(__last - 1)), __comp,             __DISTANCE_TYPE(__first));}template <class _RandomAccessIterator, class _Tp, class _Distance>inline void __pop_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,           _RandomAccessIterator __result, _Tp __value, _Distance*){  *__result = *__first;//设定尾值为首值,于是尾值即为欲求结果,                        //可由客户端稍后再以底层容器之pop back()取出尾值  __adjust_heap(__first, _Distance(0), _Distance(__last - __first), __value);}template <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--;//维护 secondChild为最大的子节点    *(__first + __holeIndex) = *(__first + __secondChild);//上移    __holeIndex = __secondChild;//__holeIndex 下移    __secondChild = 2 * (__secondChild + 1);  }  //没有右子节点 只有左子节点的情况 _secondChild < __len  if (__secondChild == __len) {    *(__first + __holeIndex) = *(__first + (__secondChild - 1));    __holeIndex = __secondChild - 1;  }  __push_heap(__first, __holeIndex, __topIndex, __value, __comp);}

sort_heap

既然每次pop_heap可获得heap中键值最大的元素,如果持续对整个heap做pop_heap操作,每次将操作范围从后向前缩减一个元素.(因为pop_heap会把键值最大的元素放在底部容器的最尾端),当整个程序执行完毕时,我们便有了一个递增序列。

template <class _RandomAccessIterator, class _Compare>void sort_heap(_RandomAccessIterator __first,          _RandomAccessIterator __last, _Compare __comp){  //以下,每执行一次pop heap ( ),极值(在STL heap中为极大值)即被放在尾端。  //扣除尾端再执行一次pop_ heap,次极值又被放在新尾端。一直下去,最后即得  //排序结果  while (__last - __first > 1)    pop_heap(__first, __last--, __comp);}

make_heap

这个算法用来将一段现有的数据转化为一个heap。其主要依据就是4.7.1节提到的complete binary tree的隐式表述(implicit representation)。

template <class _RandomAccessIterator, class _Tp, class _Distance>void __make_heap(_RandomAccessIterator __first,            _RandomAccessIterator __last, _Tp*, _Distance*){  if (__last - __first < 2) return;  _Distance __len = __last - __first;  _Distance __parent = (__len - 2)/2;//对一般的数据进行 adjust_heap      while (true) {    __adjust_heap(__first, __parent, __len, _Tp(*(__first + __parent)));    if (__parent == 0) return;    __parent--;  }}

原创粉丝点击