STL源码剖析---heap

6 篇文章 0 订阅
4 篇文章 0 订阅

Heap堆是常用的数据结构,Heap中也可以存放元素。但是STL中并没有提供Heap容器,只是提供了关于Heap操作的算法。只要支持RandomAccessIterator的容器都可以作为Heap容器。Heap的背景是建立在优先队列的基础上,priority queue允许我们任意的插入一个元素,但是会自动将元素进行排序,使得取出时一定是从优先级最高的元素开始取。我们可以考虑使用list这种数据结构完成priority queue,也就是说,我们可以轻松的完成插入(因为list其实是线性链表),但是我们要取得list中的极值时,则需要对整个list进行遍历。或者我们可以这样考虑,将元素插到list的合适位置,这样一来,虽然很容易取得极值,但是我们的搜索插入也是需要线性搜索。

   很自然的想到二分法来减少复杂度,也就是使用二叉搜索树,这样插入和求极值都值需要O(logN)的复杂度,但是这要求二叉搜索树的输入元素要随机,不能按照有序的输入,而且二叉搜索树不容易实现,所以我们选择binary heap来构造这样的容器【方便进行插入,检索,排序】
    Binary heap其实就是一棵完全二叉树,我们知道完全二叉树可以使用普通的array来实现,因为节点i的左子节点就在2i处,右子节点在2i+1处,而父节点则在i/2处,这种表述法称为隐式表述法,考虑到array无法动态改变大小,我们使用之前定义的vector来实现我们的heap.
当向heap中插入元素时,插入到末尾,“向上维护”即可:指的是把插入结点与其父结点比较,如果不符合堆得要求则交换,再向上维护其父结点……
当在heap取出元素时,把末尾元素放到Heap头,"向下维护“即可:指的是父结点与孩子结点比较,如果不满足要求,与较大(较小)一个交换,再维护交换的孩子结点……

Heap不允许遍历其结点,所以Heap没有迭代器。

push_heap 算法
为了满足complete binary tree条件,新加入的元素一定要放在最下一层作为叶子节点,并填在由左至右的第一个空格,也就是把新元素插入到底层vector的end()处。为满足max-heap的条件(每个节点的键值都大于等于其子节点键值),我们执行percolate up(上溯)程序:将新节点拿来与父节点比较,如果键值(key)比父节点大,就父子对换位置。
http://www.xpc-yx.com/wp-content/uploads/2015/01/push_heap.png
template<calss RandomAccessIterator>
inline void upSortLastElem( RandomAccessIterator first, RandomAccessIterator last)
{
	__push_heap_aux(first,last,distance_type(first),value_type(first));
}

//向上维护  
template <class RandomAccessIterator, class Distance, class T>  
void __push_heap(RandomAccessIterator first, Distance holeIndex,  
                 Distance topIndex, T value) {  
  Distance parent = (holeIndex - 1) / 2;    // 找出父结点  
  //父结点不是heap顶 且 父节点的值小于孩子结点(小于号<可以看出STL维护的是max heap)  
  while (holeIndex > topIndex && *(first + parent) < value) {  
    *(first + holeIndex) = *(first + parent);   // 父结点下调  
    holeIndex = parent; // 维护父结点  
    parent = (holeIndex - 1) / 2;     
  }      
  //已达到heap顶或者满足heap性质后,给新插入元素找到合适位置  
  *(first + holeIndex) = value;   
}  
  
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)));  
    
}  
  
//往heap添加元素。注意:此函数调用时,元素已经添加到末尾了,且迭代器已经加1了  
template <class RandomAccessIterator>  
inline void push_heap(RandomAccessIterator first, RandomAccessIterator last) {  
  // 注意,此函式被呼叫時,新元素應已置於底層容器的最尾端。  
  __push_heap_aux(first, last, distance_type(first), value_type(first));  
}  
  
//向上维护,允许用自己定义的comp函数,这是未必是max heap了  
template <class RandomAccessIterator, class Distance, class T, class Compare>  
void __push_heap(RandomAccessIterator first, Distance holeIndex,  
                 Distance topIndex, T 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算法  执行下溯程序,

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));  
  /* 
    根据implicit representation heap的次序性,pop后的结果是容器的第一个元素。 
    把最后一个元素放到到容器头,因此维护时维护区间是[first last-1)。 
  */  
}  
//取出heap顶元素,按照max heap属性来维护heap  
template <class RandomAccessIterator>  
inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last) {  
  __pop_heap_aux(first, last, value_type(first));  
}  
  
//向下维护heap,允许执行比较函数comp  
template <class RandomAccessIterator, class Distance, class T, class Compare>  
void __adjust_heap(RandomAccessIterator first, Distance holeIndex,  
                   Distance len, T 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);  
}  
sort_heap 算法
template< class RandomAccessIterator>
void sort_heap(RandomAccessIterator first, RandomAccessIterator last)
{
<span style="white-space:pre">	</span>while ( last-first>1) //while 判断的边界
<span style="white-space:pre">	</span>//堆排序只需要不断的进行pop,就可以得到有序堆
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>Pop_heap(first, last--); //不断调用pop_heap来达到效果,因为pop过程中并不是把元素删了,而是把最大的移到最后
<span style="white-space:pre">		</span>last--;
<span style="white-space:pre">	</span>}
}
make_heap算法
template<class RandomAccessIterator>
inline void make_heap( RandomAccessIterator first , RandomAccessIterator last)
{
<span style="white-space:pre">	</span>__make_heap( first, last,value_type(first), distance_type(first));
}
template<class RandomAccessIterator,class T, class Distance>
void __make_heap(RandomAccessIterator first, RandomAccessIterator last, T*,Distance*)
{
<span style="white-space:pre">	</span>if (last – first <2 ) return;
<span style="white-space:pre">	</span>Distance len = last – first;
<span style="white-space:pre">	</span>Distance parent = (len-2)/2;                                       //只能对树的内部节点进行“下溯”
<span style="white-space:pre">	</span>while(true)
<span style="white-space:pre">	</span>{
<span style="white-space:pre">	</span>  __adjust_heap( first, parent, len,T(*(first+parent)));
<span style="white-space:pre">	</span>  If(parent==0) return;
<span style="white-space:pre">	</span>  --parent;
<span style="white-space:pre">	</span>}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值