STL源码剖析 [容器](七)[stl_heap.h]

make_heap()、push_heap()、pop_heap()、sort_heap()是主要函数。

建立堆【make_heap(_First, _Last, _Comp)】:默认是建立最大堆的。对int类型,可以在第三个参数传入greater<int>()得到最小堆。

 添加数据【push_heap (_First, _Last)】:要先在容器中加入数据,再调用push_heap ()

 删除数据【pop_heap(_First, _Last)】:要先调用pop_heap()再在容器中删除数据

 堆排序【sort_heap(_First, _Last) 】:排序之后就不再是一个合法的heap了

stl_heap.h源码:

// Filename:    stl_heap.h

// Comment By:  凝霜
// E-mail:      [email protected]
// Blog:        http://blog.csdn.net/mdl13412

/*
 *
 * Copyright (c) 1994
 * Hewlett-Packard Company
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  Hewlett-Packard Company makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 *
 * Copyright (c) 1997
 * Silicon Graphics Computer Systems, Inc.
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  Silicon Graphics makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 */

/* NOTE: This is an internal header file, included by other STL headers.
 *   You should not attempt to use it directly.
 */

#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

////////////////////////////////////////////////////////////////////////////////
// 注意: push_heap()操作前要保证新添加的元素已经加入到容器末尾!!!
////////////////////////////////////////////////////////////////////////////////
// 下面是使用默认比较函数的一个实例, XXX代表需要调整结点的位置
// 执行插入前, 元素已经追加到容器尾, 其值为450, 这里我们只
// 关注其位置, 不表示出其数值
//                                 [500]
//                                   |
//                   ---------------------------------
//                   |                               |
//                 [300]                           [400]
//                   |                               |
//        -----------------------         -----------------------
//        |                     |         |                     |
//      [200]                 [270]     [350]                 [240]
//        |                     |
//   -----------           -----------
//   |         |           |         |
// [150]     [130]       [120]     [XXX]
//
// first                                                                      last
// ↓                                                                           ↓
// --------------------------------------------------------------------------------------------
// | Not Use | 500 | 300 | 400 | 200 | 270 | 350 | 240 | 150 | 130 | 120 | XXX | ...... | end |
// --------------------------------------------------------------------------------------------
//
// 下面是移动步骤及内存变化
//                                 [500]
//                                   |
//                   ---------------------------------
//                   |                               |
//                 [300]                           [400]
//                   |                               |
//        -----------------------                -----------------------
//        |                     |                |                     |
//      [200]                 [XXX]-------     [350]                 [240]
//        |                     |        |
//   -----------           -----------   |
//   |         |           |         |   |  调整元素位置
// [150]     [130]       [120]     [270]--
//
// first                                                                      last
// ↓                                                                           ↓
// --------------------------------------------------------------------------------------------
// | Not Use | 500 | 300 | 400 | 200 | XXX | 350 | 240 | 150 | 130 | 120 | 270 | ...... | end |
// --------------------------------------------------------------------------------------------
//
//                                 [500]
//                                   |
//                   ---------------------------------
//                   |                               |
//                 [XXX]------------- 交换         [400]
//                   |              |                |
//        -----------------------   |     -----------------------
//        |                     |   |     |                     |
//      [200]                 [300]--   [350]                 [240]
//        |                     |
//   -----------           -----------
//   |         |           |         |
// [150]     [130]       [120]     [270]
//
// first                                                                      last
// ↓                                                                           ↓
// --------------------------------------------------------------------------------------------
// | Not Use | 500 | XXX | 400 | 200 | 300 | 350 | 240 | 150 | 130 | 120 | 270 | ...... | end |
// --------------------------------------------------------------------------------------------
//
// 现在满足heap的要求了, 对[XXX]直接赋值即可
//
////////////////////////////////////////////////////////////////////////////////

template <class RandomAccessIterator, class Distance, class T>
void __push_heap(RandomAccessIterator first, Distance holeIndex,
                 Distance topIndex, T value)
{
  // 首先找出待处理元素的父结点
  Distance parent = (holeIndex - 1) / 2;

  // 判断当前待处理结点是否优先级高于其父结点, 如果是则将其父结点向下移动
  // 设置当前结点为父结点位置, 继续, 直到优先级小于父结点或者已经到达heap顶端
  while (holeIndex > topIndex && *(first + parent) < value) {
    *(first + holeIndex) = *(first + parent);
    holeIndex = parent;
    parent = (holeIndex - 1) / 2;
  }

  // 将找到的合适的位置设置成正确值
  *(first + holeIndex) = value;
}

template <class RandomAccessIterator, class Distance, class T>
inline void __push_heap_aux(RandomAccessIterator first,
                            RandomAccessIterator last, Distance*, T*)
{
  // 因为first所指的那个元素不是heap的组成元素, 所以计算距离要减去1
  __push_heap(first, Distance((last - first) - 1), Distance(0),
              T(*(last - 1)));
}

// 调用此函数前要先把待处理元素追加到容器末尾
template <class RandomAccessIterator>
inline void push_heap(RandomAccessIterator first, RandomAccessIterator last)
{
  __push_heap_aux(first, last, distance_type(first), value_type(first));
}

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

template <class RandomAccessIterator, class Compare, class Distance, class T>
inline void __push_heap_aux(RandomAccessIterator first,
                            RandomAccessIterator last, Compare comp,
                            Distance*, T*)
{
  __push_heap(first, Distance((last - first) - 1), Distance(0),
              T(*(last - 1)), comp);
}

// 这个除了用户自己指定优先级决策判别式外和默认的无区别
template <class RandomAccessIterator, class Compare>
inline void push_heap(RandomAccessIterator first, RandomAccessIterator last,
                      Compare comp)
{
  __push_heap_aux(first, last, comp, distance_type(first), value_type(first));
}

////////////////////////////////////////////////////////////////////////////////
// 注意: pop_heap()操作, 执行完操作后要自己将容器尾元素弹出
////////////////////////////////////////////////////////////////////////////////
// 这里以默认的heap优先级决策来说
// STL采用的是先将待pop的元素复制到heap尾部, 然后将整个heap向上调整
// 这样就会将最后空出一个hole, 将原来最后的元素在这里进行push()操作
// 这就是两个shift_up的过程
// 个人感觉使用使用shift_down的算法更高效, 虽然时间复杂度一样, 但是shift_down
// 进行操作的元素会更少,
// 之所以用shift_up这可能也是STL设计理念的问题吧, 能复用就不写新的^_^
////////////////////////////////////////////////////////////////////////////////
// 下面是使用默认比较函数的一个实例, 我们要弹出的是优先级最高的元素[500]
// 首先要把弹出的元素[500]复制到heap末尾
// 然后进行第一次shift_up, 完成后进行push()操作, 这个就是第二次shift_up了
//
//                                 [500]
//                                   |
//                   ---------------------------------
//                   |                               |
//                 [300]                           [400]
//                   |                               |
//        -----------------------         -----------------------
//        |                     |         |                     |
//      [200]                 [270]     [350]                 [240]
//        |                     |
//   -----------           -----------
//   |         |           |         |
// [150]     [130]       [120]     [100]
//
// first                                                                      last
// ↓                                                                           ↓
// --------------------------------------------------------------------------------------------
// | Not Use | 500 | 300 | 400 | 200 | 270 | 350 | 240 | 150 | 130 | 120 | 100 | ...... | end |
// --------------------------------------------------------------------------------------------
//
// 下面是移动步骤及内存变化                      复制
//                                 [500]----------------------------------
//                                   |                                   |
//                   ---------------------------------                   |
//                   |                               |                   |
//                 [300]                           [400]                 |
//                   |                               |                   |
//        -----------------------         -----------------------        |
//        |                     |         |                     |        |
//      [200]                 [270]     [350]                 [240]      |
//        |                     |                                        |
//   -----------           -----------                                   |
//   |         |           |         |                                   |
// [150]     [130]       [120]     [500]----------------------------------
//
// first                                                                      last
// ↓                                                                           ↓
// --------------------------------------------------------------------------------------------
// | Not Use | 500 | 300 | 400 | 200 | 270 | 350 | 240 | 150 | 130 | 120 | 500 | ...... | end |
// --------------------------------------------------------------------------------------------
//
//                                 [400]-----------------------
//                                   |                        |
//                   ---------------------------------        | shift_up
//                   |                               |        |
//                 [300]                           [400]-------
//                   |                               |
//        -----------------------         -----------------------
//        |                     |         |                     |
//      [200]                 [270]     [350]                 [240]
//        |                     |
//   -----------           -----------
//   |         |           |         |
// [150]     [130]       [120]     [500]
//
// first                                                                      last
// ↓                                                                           ↓
// --------------------------------------------------------------------------------------------
// | Not Use | 400 | 300 | 400 | 200 | 270 | 350 | 240 | 150 | 130 | 120 | 500 | ...... | end |
// --------------------------------------------------------------------------------------------
//
//                                 [400]
//                                   |
//                   ---------------------------------
//                   |                               |        shift_up
//                 [300]                           [350]-------------------
//                   |                               |                    |
//        -----------------------         -----------------------         |
//        |                     |         |                     |         |
//      [200]                 [270]     [350]                 [240]       |
//        |                     |         |                               |
//   -----------           -----------    ----------------------------------
//   |         |           |         |
// [150]     [130]       [120]     [500]
//
// first                                                                      last
// ↓                                                                           ↓
// --------------------------------------------------------------------------------------------
// | Not Use | 400 | 300 | 350 | 200 | 270 | 350 | 240 | 150 | 130 | 120 | 500 | ...... | end |
// --------------------------------------------------------------------------------------------
//
// 接下来就是push()操作了, 参考前面的push()
////////////////////////////////////////////////////////////////////////////////

template <class RandomAccessIterator, class Distance, class T>
void __adjust_heap(RandomAccessIterator first, Distance holeIndex,
                   Distance len, T value)
{
  Distance topIndex = holeIndex;
  Distance secondChild = 2 * holeIndex + 2;     // 弹出元素的有子孩

  // 调整heap元素位置
  while (secondChild < len) {
    // 选择两个子孩中较大的进行操作, 使用secondChild表示其偏移
    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;
  }

  // 这里就是shift_up过程了, 将最初的heap末尾元素向上调整
  // 侯捷老师对这里的理解有误, :-), 人非圣贤, 孰能无过, ^_^
  __push_heap(first, holeIndex, topIndex, value);
}

template <class RandomAccessIterator, class T, class Distance>
inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last,
                       RandomAccessIterator result, T value, Distance*)
{
  // 将弹出的元素调整到heap末尾, 这个元素需要用户手动弹出
  *result = *first;

  // 去掉末尾哪个弹出的元素, 调整heap
  __adjust_heap(first, Distance(0), Distance(last - first), value);
}

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

template <class RandomAccessIterator>
inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last)
{
  __pop_heap_aux(first, last, value_type(first));
}

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

template <class RandomAccessIterator, class T, class Compare, class Distance>
inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last,
                       RandomAccessIterator result, T value, Compare comp,
                       Distance*)
{
  *result = *first;
  __adjust_heap(first, Distance(0), Distance(last - first), value, comp);
}

template <class RandomAccessIterator, class T, class Compare>
inline void __pop_heap_aux(RandomAccessIterator first,
                           RandomAccessIterator last, T*, Compare comp)
{
  __pop_heap(first, last - 1, last - 1, T(*(last - 1)), comp,
             distance_type(first));
}

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 T, class Distance>
void __make_heap(RandomAccessIterator first, RandomAccessIterator last, T*,
                 Distance*)
{
  if (last - first < 2) return;
  Distance len = last - first;
  Distance parent = (len - 2)/2;

  while (true) {
    __adjust_heap(first, parent, len, T(*(first + parent)));
    if (parent == 0) return;
    parent--;
  }
}

template <class RandomAccessIterator>
inline void make_heap(RandomAccessIterator first, RandomAccessIterator last)
{
  __make_heap(first, last, value_type(first), distance_type(first));
}

template <class RandomAccessIterator, class Compare, class T, class Distance>
void __make_heap(RandomAccessIterator first, RandomAccessIterator last,
                 Compare comp, T*, Distance*)
{
  if (last - first < 2) return;
  Distance len = last - first;
  Distance parent = (len - 2)/2;

  while (true) {
    __adjust_heap(first, parent, len, T(*(first + parent)), comp);
    if (parent == 0) return;
    parent--;
  }
}

template <class RandomAccessIterator, class Compare>
inline void make_heap(RandomAccessIterator first, RandomAccessIterator last,
                      Compare comp)
{
  __make_heap(first, last, comp, value_type(first), distance_type(first));
}

// 这个能保证heap有序, 其实个人感觉没啥必要, 这样还不如直接用平衡二叉树
template <class RandomAccessIterator>
void sort_heap(RandomAccessIterator first, RandomAccessIterator last)
{
  while (last - first > 1) pop_heap(first, last--);
}

template <class RandomAccessIterator, class Compare>
void sort_heap(RandomAccessIterator first, RandomAccessIterator last,
               Compare comp)
{
  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:

使用例子:

#include <cstdio>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
void PrintfVectorInt(vector<int> &vet)
{
	for (vector<int>::iterator pos = vet.begin(); pos != vet.end(); pos++)
		printf("%d ", *pos);
	putchar('\n');
}
int main()
{
	const int MAXN = 20;
	int a[MAXN];
	int i;
	for (i = 0; i < MAXN; ++i)
		a[i] = rand() % (MAXN * 2);

	//动态申请vector 并对vector建堆
	vector<int> *pvet = new vector<int>(40);
	pvet->assign(a, a + MAXN);

	//建堆
	make_heap(pvet->begin(), pvet->end());
	PrintfVectorInt(*pvet);

	//加入新数据 先在容器中加入,再调用push_heap()
	pvet->push_back(25);
	push_heap(pvet->begin(), pvet->end());
	PrintfVectorInt(*pvet);

	//删除数据  要先调用pop_heap(),再在容器中删除
	pop_heap(pvet->begin(), pvet->end());
	pvet->pop_back();
	pop_heap(pvet->begin(), pvet->end());
	pvet->pop_back();
	PrintfVectorInt(*pvet);

	//堆排序
	sort_heap(pvet->begin(), pvet->end());
	PrintfVectorInt(*pvet);

	delete pvet;
	return 0;
}

heap排序堆排序快速排序归并排序来进行个性能测试:

#include <cstdio>
#include <algorithm>
#include <ctime>
using namespace std;
//------------------------快速排序----------------------------
void quick_sort(int s[], int l, int r)
{
	if (l < r)
	{
		int i = l, j = r, x = s[l];
		while (i < j)
		{
			while(i < j && s[j] >= x) // 从右向左找第一个小于x的数
				j--;  
			if(i < j) 
				s[i++] = s[j];

			while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数
				i++;  
			if(i < j) 
				s[j--] = s[i];
		}
		s[i] = x;
		quick_sort(s, l, i - 1); // 递归调用 
		quick_sort(s, i + 1, r);
	}
}
//------------------------归并排序----------------------------
//将有二个有序数列a[first...mid]和a[mid...last]合并。
void mergearray(int a[], int first, int mid, int last, int temp[])
{
	int i = first, j = mid + 1;
	int m = mid,   n = last;
	int k = 0;

	while (i <= m && j <= n)
	{
		if (a[i] < a[j])
			temp[k++] = a[i++];
		else
			temp[k++] = a[j++];
	}

	while (i <= m)
		temp[k++] = a[i++];

	while (j <= n)
		temp[k++] = a[j++];

	for (i = 0; i < k; i++)
		a[first + i] = temp[i];
}
void mergesort(int a[], int first, int last, int temp[])
{
	if (first < last)
	{
		int mid = (first + last) / 2;
		mergesort(a, first, mid, temp);    //左边有序
		mergesort(a, mid + 1, last, temp); //右边有序
		mergearray(a, first, mid, last, temp); //再将二个有序数列合并
	}
}
bool MergeSort(int a[], int n)
{
	int *p = new int[n];
	if (p == NULL)
		return false;
	mergesort(a, 0, n - 1, p);
	return true;
}
//------------------------堆排序---------------------------
inline void Swap(int &a, int &b)
{
	int c = a;
	a = b;
	b = c;
}
//建立最小堆
//  从i节点开始调整,n为节点总数 从0开始计算 i节点的子节点为 2*i+1, 2*i+2
void MinHeapFixdown(int a[], int i, int n)
{
	int j, temp;

	temp = a[i];
	j = 2 * i + 1;
	while (j < n)
	{
		if (j + 1 < n && a[j + 1] < a[j]) //在左右孩子中找最小的
			j++;

		if (a[j] >= temp)
			break;

		a[i] = a[j];     //把较小的子结点往上移动,替换它的父结点
		i = j;
		j = 2 * i + 1;
	}
	a[i] = temp;
}
//建立最小堆
void MakeMinHeap(int a[], int n)
{
	for (int i = n / 2 - 1; i >= 0; i--)
		MinHeapFixdown(a, i, n);
}
void MinheapsortTodescendarray(int a[], int n)
{
	for (int i = n - 1; i >= 1; i--)
	{
		Swap(a[i], a[0]);
		MinHeapFixdown(a, 0, i);
	}
}
const int MAXN = 5000000;
int a[MAXN];
int b[MAXN], c[MAXN], d[MAXN];
int main()
{
	int i;
	srand(time(NULL));
	for (i = 0; i < MAXN; ++i)
		a[i] = rand() * rand(); //注rand()产生的数在0到0x7FFF之间

	for (i = 0; i < MAXN; ++i)
		d[i] = c[i] = b[i] = a[i];

    clock_t ibegin, iend;

	printf("--当前数据量为%d--By MoreWindows(http://blog.csdn.net/MoreWindows)--\n", MAXN);
	//快速排序
	printf("快速排序:  ");
	ibegin = clock();
	quick_sort(a, 0, MAXN - 1);
	iend = clock();
	printf("%d毫秒\n", iend - ibegin);

	
	//归并排序
	printf("归并排序:  ");
	ibegin = clock();
	MergeSort(b, MAXN);
	iend = clock();
	printf("%d毫秒\n", iend - ibegin);

	//堆排序
	printf("堆排序:  ");
	ibegin = clock();
	MakeMinHeap(c, MAXN);
	MinheapsortTodescendarray(c, MAXN);
	iend = clock();
	printf("%d毫秒\n", iend - ibegin);

	//STL中的堆排序
	printf("STL中的堆排序: ");	
	ibegin = clock();
	make_heap(d, d + MAXN);
	sort_heap(d, d + MAXN);
	iend = clock();
	printf("%d毫秒\n", iend - ibegin);
	return 0;
}


 

你可能感兴趣的:(STL源码剖析 [容器](七)[stl_heap.h])