堆排序

堆排序就是对简单选择排序进行的一种改进。(调整堆的过程就是选择待排序序列中的最大值,本质和选择排序是一样的)。
记录的比较与交换是跳跃式进行,因此堆排序是一种不稳定的原地排序方法。
不适合待排序序列个数较少的情况。
在堆排序算法中,我们使用的是大顶堆,小顶堆通常在构造优先队列时使用。

堆结构
堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子的结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。

堆排序基本思想:利用堆进行排序。将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根节点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次小值。如此反复执行,便能得到一个有序序列。

主要解决两个问题:
1.如何由一个无序序列构建成一个堆?
2.如果在输出堆顶元素后,调整剩余元素成为一个新的堆?

C++实现

/********************************/
/***********堆排序***************/
/********************************/
void HeapAdjust(ElemType a[],int s,int m)
{
	int temp,j;
	temp = a[s];
	for(j = 2*s;j <= m;j *= 2)  //沿关键字较大的孩子节点向下筛选
	{
		//当前节点s,左孩子为2s,右孩子为2s+1,所以j=2*s
		if(j < m && a[j] < a[j+1])
			++j;	//j为关键字中较大的记录下标
		if(temp >= a[j])
			break;
		a[s] = a[j];
		s = j;
	}
	a[s] = temp; //插入
}

void HeapSort(ElemType a[],int n)
{
	int i;
	//第一个循环将现在的待排序序列构建成一个大顶堆
	for(i = n/2;i >= 0;i--) //i>=0
	{
		HeapAdjust(a,i,n-1);
	}
	//第二个循环将每个最大值的根节点与末尾元素交换,并在调整为大顶堆
	for(i = n-1;i > 0;i--)
	{
		swap(a,0,i);	//将堆顶记录和当前未经排序子序列的最后一个记录交换
		HeapAdjust(a,0,i-1);//将a[0..i-1]重新调整为大顶堆
	}
}

Python实现

#-------------#
#--Heap sort--#
#-------------#			
def heap_adjust(arr,s,m):
	rc = arr[s]
	j = 2*s
	while j <= m :
		if  j < m and arr[j] < arr[j+1]:
			j += 1
		if rc >= arr[j]:
			break
		arr[s] = arr[j]
		s = j
		j *= 2
	arr[s] = rc
	
def heap_sort(arr):
	arrlen = len(arr)
	for i in reversed(range(0,arrlen/2+1)): #range:arrlen/2+1?
		heap_adjust(arr,i,arrlen-1)
	for i in reversed(range(1,arrlen)):
		swap(arr,0,i)
		heap_adjust(arr,0,i-1)

堆排序复杂度分析:
运行时间消耗主要消耗在初始构建堆和重建堆时的反复筛选上。
构建整个堆的时间复杂度为o(n)。
排序时候第i次堆顶记录重建堆需要o(logi)的时间,需要取n-1次堆顶记录,所以重建堆的时间复杂度为o(nlogn)。
堆排序的时间复杂度为o(nlogn)。对排序状态不敏感,无论是最好、最坏和平均时间复杂度均为o(nlogn)。
空间复杂度o(1)。

优先级队列
堆的一个常见应用:作为高效的优先级队列。
优先级队列是一种用来维护由一组元素构成的集合S的数据结构,这一组元素中的每一个都有一个关键字key。
最大优先级队列的一个应用是在一台分时计算机上进行作业调度。

Leave a Reply

Your email address will not be published. Required fields are marked *