码迷,mamicode.com
首页 > 其他好文 > 详细

STL之heap 堆

时间:2015-07-20 16:43:31      阅读:117      评论:0      收藏:0      [点我收藏+]

标签:stl

STL中与堆相关的4个函数——建立堆make_heap(),在堆中添加数据push_heap(),在堆中删除数据pop_heap()和堆排序sort_heap():


头文件 #include <algorithm>

下面的_First与_Last为可以随机访问的迭代器(指针),_Comp为比较函数(仿函数),其规则——如果函数的第一个参数小于第二个参数应返回true,否则返回false。

建立堆

make_heap(_First, _Last, _Comp)

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

cmp函数格式如下:

bool cmp(int a,int b)

{
return a>b;
}

或者如我另一篇博文:http://blog.csdn.net/kaisa158/article/details/46853939


在堆中添加数据

push_heap (_First, _Last)

要先在容器中加入数据,再调用push_heap ()



在堆中删除数据

pop_heap(_First, _Last)

要先调用pop_heap()再在容器中删除数据



堆排序

sort_heap(_First, _Last)

排序之后就不再是一个合法的heap了


堆的详细介绍:

堆分为大顶堆和小顶堆。这里介绍并实现的是大顶堆。

堆的主要相关算法介绍

push_heap算法

此操作是向堆中添加一个节点。为了满足完全二叉树的条件,新加入的元素一定放在最下面的一层作为叶节点,并填补在由左至右的第一个空格,在这里放在底层容器vector的end()处。
很显然,新元素的加入很可能使得堆不在满足大顶堆的性质---每个节点的键值都大于或等于其子节点的键值。为了调整使得其重新满足大顶堆的特点,在这里执行一个上溯(percolate up)操作:将新节点与父节点比较,如果其键值比父节点大,就交换父子的位置,如此一直上溯,直到不需要交换或者到根节点为止。

pop_heap算法

此操作取走根节点。对于大顶堆,取得的是堆中值最大的节点,对于小顶堆,取得的是堆中值最小的节点。STL实现并不是将这个节点直接删除,而是将其放在底层容器vector的尾端。而原尾端的节点插入到前面的适当位置。
我们首先保存原vector尾端的节点值,然后将根节点值存储在此处。为了实将原尾端节点的值插入适当位置,重新构建大顶堆,我们实施如下调整堆的操作:
先执行下溯(percolate down)操作:从根节点开始将空洞节点(一开始是根节点)和较大子节点交换,并持续向下进行,直到到达叶节点为止。然后将已保存的原容器vector尾端节点赋给这个已到达叶层的空洞节点。
注意,到这里并没有结束。因为这时候可能还没有满足大顶堆的特性。还需要执行一次上溯操作。这样,便重新构建了大顶堆。

make_heap算法

此操作是依据已有的各元素构建堆。
其中,各元素已经存放在底层容器vector中。
构建堆实质是一个不断调整堆(即前面pop_heap算法中的调整堆的操作)的过程---通过不断调整子树,使得子树满足堆的特性来使得整个树满足堆的性质。
叶节点显然需要调整,第一个需要执行调整操作的子树的根节点是从后往前的第一个非叶结点。从此节点往前到根节点对每个子树执行调整操作,即可构建堆。

sort_heap算法

堆排序算法。执行此操作之后,容器vector中的元素按照从小到大的顺序排列。
构建大顶堆之后,不断执行pop_heap算法取出堆顶的元素,即可。因为每次取得的是最大的元素,将其放在容器vector的最尾端。所以到最后vector中的元素是从小到大排列的。


#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);//将区间[first,last)的元素赋值到当前的vector容器中,
    //或者赋n个值为x的元素到vector容器中,这个容器会清除掉vector容器中以前的内容。

	//建堆
	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;
}

掌握其基本用法后,我们用这个堆排序和《白话经典算法系列》中的堆排序快速排序归并排序来进行个性能测试

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

对100000(十万)个数据的测试结果:

技术分享

对500000(五十万)个数据的测试结果:

技术分享

对1000000(一百万)个数据的测试结果:

技术分享

对5000000(五百万)个数据的测试结果:

技术分享

版权声明:本文为博主原创文章,未经博主允许不得转载。

STL之heap 堆

标签:stl

原文地址:http://blog.csdn.net/kaisa158/article/details/46967707

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!