算法导论学习之——堆排序

    在这一篇文章中,首先介绍一下堆的属性和性质。然后讲解一下建堆的过程,最后讲解堆排序。


1、堆的介绍

    堆的物理存储结构就是一个一维的数组,其数据结构就是一个完全的二叉树。需要注意的是堆中的每个结点不需要后继指针,其父节点和左右孩子结点都可以通过计算得到。假设要计算结点i(i为数组的下标)的父节点和左右孩子结点,可以使用以下公式计算:


在此计算的都是结点的数组下标,

由于堆的数据结构是一个完全二叉树,设该完全二叉树有n个结点。则其内部结点下标分别为:1,2,。。。

故其叶子结点下标分别为:

堆高度:就是从根节点到最长叶子结点的距离。包含N个结点的堆高为:

其次是最大堆和最小堆问题,最大堆就是对任意的父节点的值总是大于或等于孩子节点的值,这样的话就能保证根节点保存堆中的最大元素。

即:A[parent(i)] >=A[i]    (最大堆)

同样,最小堆就是对任意的父节点的值小于或等于孩子结点的值,这样就能保证根节点的值最小。

即:A[parent(i)] <=A[i]  (最小堆)

一般情况下,最大堆用于堆排序中,最小堆用于优先队列中。

2、建堆

根据堆的性质,我们最终要得到堆的根节点是一个最值,因而,我们需要自底向上进行建堆,若自顶向下建堆的话则不能保证根节点是最值。在此仅讨论建立最大堆的情况。

首先我们可以认为每个叶子结点已经是一个最大堆,然后从最末一个非叶子结点开始进行对调整,以满足最大堆的性质。直到到达第一个结点即根节点。

在建堆中,主要有二个过程,一个是堆调整,另一个是建堆。在此,首先介绍一下对调整的过程,首先,假如要调整的结点为i,并且结点i的左右孩子已经都是最大堆。于是,我们首先计算A[i],A[ left(i) ],A[ right(i) ]的大小。1)若A[i]最大,则无需调整。2)若A[ left(i) ]最大,则将A[i]与A[ left(i) ]交换,此时最大值成为了父节点,而A[ i]到了原来的A[ left(i) ]的位置。然后又将A[ i ]与其左右孩子进行比较,直到叶子结点。同样,若A[right(i) ]最大,其操作同理可得。

void max_heapify(int A[],int i)
{
	l=2*i;		//l = left(i)
	r=2*i+1;		//r = right(i)
	//heapSize为堆中实际元素的个数 
	if(l <= heapSize && A[l] > A[i])	//largest为i和l中的最大下标值 
		largest = l;
	else largest = i;
	if(l <= heapSize && A[r] > A[largest])
		largest = r;
	
	if(i != largest){
		swap(A[i], A[largest]);		//交换 A[i]与 A[largest]
		max_heapify(A, largest);
	}
}
建堆的代码如下:

void build_max_heapify(int A[])
{
	for(int i=A.length/2; i>=1; i--)
	{
		max_heapify(A, i);
	}
}

简单估算一下:我们在调用max_heapify的时间复杂度为:O(lgn),调用build_max_heapify的次数为:O(n)。因而,整个建堆的时间复杂度为:O(nlgn)。

不过这个时间复杂度不是渐进紧确的,因为在调用max_heapify时并不是所有的结点高度都为lgn。实际上大部分结点的高度都很小,更准确的说,高度为h的结点个数至多有个,而在高度为h的结点上运行max_heapify的代价为:O(h)。因此,我们可以计算build_max_heapify的总代价为:






最后得到构造一个最大堆的时间复杂度为:O(n)。即在线性时间内,可以把一个无序数组构造成为一个最大堆。

3、堆排序

    排序思想:首先使用build_max_heapify将数组A[1,2,…,n]建成最大堆,然后将A[1]与A[n]的互换,然后再使用max_heapify调整数组A[1,2,…,n-1]成最大堆,然后将A[1]与A[n-1]互换,依次类推,最终得到一个升序的数组A[1,2,…,n]。

void heapSort()
{
	build_max_heapify(A);
	int heapSize = A.length;	//heapSize=A.length-size
	while(heapSize > 1)
	{
		swap(A[1], A[heapSize]);	//交换 A[1]与 A[heapSize]
		heapSize --; 
		max_heapify(A,1); 
	} 
}
时间代价分析:build_max_heapify的时间代价是:O(n), 调用了n-1次的max_heapify,每次的时间为:O(lgn)。故heapSort的总时间复杂度为:O(nlgn)。


不知道公式怎么加到文章中,这个暂时做成图片传上来了,导致文章的排版比较乱!


posted @ 2014-03-08 14:46  liuwu265  阅读(756)  评论(0编辑  收藏  举报