【STL源码剖析读书笔记】【第4章】序列式容器之heap和priority_queue

一、heap

1、heap概述

heap并不归属于STL容器组件,扮演priority queue的助手,binary max heap适合作为priority queue的底层机制。

binary heap是一种completebinary tree,整棵binary tree除了最底层的叶子节点外是填满的,而最底层的叶子节点由左至右不得有空隙。

利用array来存储completebinary tree的所有节点,将array#0元素保留,那么当complete binary tree的某个节点位于arrayi处时,其左子节点必位于array2i处,右子节点必位于array2i+1处,父节点必位于i/2处。

heap分为max-heapmin-heap两种,前者每个节点的键值都大于或等于其子节点的值,后者每个节点的键值都小于或等于其子节点的值。max-heap中最大值在根节点,min-heap最小值在根节点。底层存储结构为vector或者array

2、push_heap算法


3、pop_heap算法

4、sort_heap算法


5、make_heap算法

将一段现有的数据转化为一个heap

6、heap源码

//在STL中,heap不作为容器,只是提供了关于Heap操作的算法。
//heap没有自己的迭代器,只要支持RandomAccessIterator的容器都可以作为Heap容器
#ifndef __SGI_STL_INTERNAL_HEAP_H
#define __SGI_STL_INTERNAL_HEAP_H

__STL_BEGIN_NAMESPACE

#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
#pragma set woff 1209
#endif

// Heap-manipulation functions: push_heap, pop_heap, make_heap, sort_heap.

//堆中添加元素;关于push_heap操作的原型有两个
//注意: push_heap()操作之前必须保证新添加的元素已经加入到容器末尾
//***************************************************************
//*		第一个版本使用operator<操作
//*		template< class RandomIt >
//*		void push_heap( RandomIt first, RandomIt last );
//***************************************************************
//*		第二个版本使用比较函数comp
//*		template< class RandomIt, class Compare >
//*		void push_heap( RandomIt first, RandomIt last,Compare comp );
//***************************************************************
//*	比较函数comp:comparison function which returns true if the first argument is less than the second. 
//*	The signature of the comparison function should be equivalent to the following:
//*		bool cmp(const Type1 &a, const Type2 &b);
//***************************************************************
template <class _RandomAccessIterator, class _Distance, class _Tp>
void 
__push_heap(_RandomAccessIterator __first,
            _Distance __holeIndex, _Distance __topIndex, _Tp __value)
{//当前节点标号为__holeIndex- 1即为新插入元素标号,因为根节点标号是从0开始,所以这里要-1
  _Distance __parent = (__holeIndex - 1) / 2;//找出当前节点的父节点
  //尚未达到根节点,且所插入数据value大于父节点的关键字值  
  while (__holeIndex > __topIndex && *(__first + __parent) < __value) {
    *(__first + __holeIndex) = *(__first + __parent);//交换当前节点元素与其父节点元素的值
    __holeIndex = __parent;//更新当前节点标号,上溯
    __parent = (__holeIndex - 1) / 2;//更新父节点
  }   //持续达到根节点,或满足heap的性质
  *(__first + __holeIndex) = __value;//插入正确的位置
}

template <class _RandomAccessIterator, class _Distance, class _Tp>
inline void 
__push_heap_aux(_RandomAccessIterator __first,
                _RandomAccessIterator __last, _Distance*, _Tp*)
{
	//__last - __first) - 1表示插入后元素的个数,也是容器的最后一个下标数字
	//新插入的元素必须位于容器的末尾
	__push_heap(__first, _Distance((__last - __first) - 1), _Distance(0), 
              _Tp(*(__last - 1)));
}

//第一个版本push_heap默认是operator<操作
template <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);
}
//第二个版本push_heap自定义比较操作函数comp
template <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()操作, 执行完操作后要自己将容器尾元素弹出
//default (1):	
//			template <class RandomAccessIterator>
//			void pop_heap (RandomAccessIterator first, RandomAccessIterator last);
//custom (2):	
//			template <class RandomAccessIterator, class Compare>
//			void pop_heap (RandomAccessIterator first, RandomAccessIterator last,
//                 Compare comp);
//***********************************************************************
template <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) {//若子节点标号比总的标号数小
    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, class _Tp, class _Distance>
inline void 
__pop_heap(_RandomAccessIterator __first, _RandomAccessIterator __last,
           _RandomAccessIterator __result, _Tp __value, _Distance*)
{
  *__result = *__first;//把原始堆的根节点元素放在容器的末尾
  //调整剩下的节点元素,使其成为新的heap
  __adjust_heap(__first, _Distance(0), _Distance(__last - __first), __value);
}

template <class _RandomAccessIterator, class _Tp>
inline void 
__pop_heap_aux(_RandomAccessIterator __first, _RandomAccessIterator __last,
               _Tp*)
{
  __pop_heap(__first, __last - 1, __last - 1, 
             _Tp(*(__last - 1)), __DISTANCE_TYPE(__first));
}

template <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));
}

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--;
    *(__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));
}

template <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);
}


//创建堆
//default(1):	
//			template <class RandomAccessIterator>
//			void make_heap (RandomAccessIterator first, RandomAccessIterator last);
//custom (2):	
//			template <class RandomAccessIterator, class Compare>
//			void make_heap (RandomAccessIterator first, RandomAccessIterator last,Compare comp );
  //********************************************************************************
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;
    
  while (true) {
    __adjust_heap(__first, __parent, __len, _Tp(*(__first + __parent)));
    if (__parent == 0) return;
    __parent--;
  }
}

template <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--;
  }
}

template <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));
}

//排序堆里面的内容
//default(1):	
//			template <class RandomAccessIterator>
//			void sort_heap (RandomAccessIterator first, RandomAccessIterator last);
//custom (2):	
//			template <class RandomAccessIterator, class Compare>
//			void sort_heap (RandomAccessIterator first, RandomAccessIterator last,
//						  Compare comp);
//**************************************************************************
template <class _RandomAccessIterator>
void sort_heap(_RandomAccessIterator __first, _RandomAccessIterator __last)
{
  __STL_REQUIRES(_RandomAccessIterator, _Mutable_RandomAccessIterator);
  __STL_REQUIRES(typename iterator_traits<_RandomAccessIterator>::value_type,
                 _LessThanComparable);
  while (__last - __first > 1)
    pop_heap(__first, __last--);//每次取出根节点元素,直到heap为空
}

template <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);
}

#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
#pragma reset woff 1209
#endif

__STL_END_NAMESPACE

#endif /* __SGI_STL_INTERNAL_HEAP_H */

// Local Variables:
// mode:C++
// End:

7、heap测试实例

#include<vector>
#include<iostream>
#include<algorithm> //heap algorithm
using namespace std;

int main(void){
	{   //test heap(底层以vector完成)
		int ia[9] = { 0, 1, 2, 3, 4, 8, 9, 3, 5 };
		vector<int> ivec(ia, ia + 9);

		make_heap(ivec.begin(), ivec.end());
		for (int i = 0; i < ivec.size(); ++i)
			cout << ivec[i] << ' ';
		cout << endl;

		ivec.push_back(7);
		push_heap(ivec.begin(), ivec.end());
		for (int i = 0; i < ivec.size(); ++i)
			cout << ivec[i] << ' ';
		cout << endl;

		pop_heap(ivec.begin(), ivec.end());
		cout << ivec.back() << endl;
		ivec.pop_back();

		for (int i = 0; i < ivec.size(); ++i)
			cout << ivec[i] << ' ';
		cout << endl;

		sort_heap(ivec.begin(), ivec.end());
		for (int i = 0; i < ivec.size(); ++i)
			cout << ivec[i] << ' ';
		cout << endl;
	}
	{   //test heap (底层以array 完成)
		int ia[9] = { 0, 1, 2, 3, 4, 8, 9, 3, 5 };
		make_heap(ia, ia + 9);


		sort_heap(ia, ia + 9);
		for (int i = 0; i < 9; ++i)
		{
			cout << ia[i] << ' ';
		}
		cout << endl;
    }
	{   //test heap (底层以array完成)
		int ia[6] = { 4, 1, 7, 6, 2, 5 };
		make_heap(ia, ia + 6);
		for (int i = 0; i < 6; ++i)
			cout << ia[i] << ' ';
		cout << endl;
	}
	system("pause");
	return 0;
}

二、priority_queue

1、priority_queue概述

priority_queue是一个拥有权值观念的queue,它允许加入新元素、移除旧元素、审视元素值等功能。只允许在底端加入元素,并从顶端取出元素,除此之外别无其它存取元素的途径。

priority_queue缺省情况下是以vector为底层容器,再加上heap处理规则,STLpriority_queue往往不被归类为container(容器),而被归类为containeradapter。

2、测试实例

#include<queue>
#include<iostream>
#include<algorithm>
using namespace std;

int main(void){
	//test priority queue...
	int ia[9] = { 0, 1, 2, 3, 4, 8, 9, 3, 5 };
	priority_queue<int> ipq(ia, ia + 9);
	cout << "size= " << ipq.size() << endl;

	for (int i = 0; i < ipq.size(); ++i)
		cout << ipq.top() << ' ';
	cout << endl;

	while (!ipq.empty()){
		cout << ipq.top() << ' ';
		ipq.pop();
	}
	cout << endl;

	system("pause");
	return 0;
}


原文地址:https://www.cnblogs.com/ruan875417/p/4558316.html