排序算法之堆排序(优先队列)

Posted wzb的QQ空间

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了排序算法之堆排序(优先队列)相关的知识,希望对你有一定的参考价值。

1、堆排序的堆,其实是一个 完全二叉树。既是一个结点要么是叶子结点,要么必定有左右两个子节点的树。

2、堆有序:每个结点的值,都必须大于两个子节点。但是两个子结点的大小不作要求。

3、一棵大小为N的完全二叉树,高度为lgN(层)。

 

用数组实现堆,假设数组下标从0开始,下标为k的元素,它的左子树是2k+1,右子树是左子树+1,即2k+2

 

一:由上至下的有序化(下沉)

如果堆的有序状态,因为某个结点比它的两个子结点或者其中之一小而打破了,那么可以通过与两个子结点中的较大者来交换。

交换后可能会在子结点处打破原有序状态,那么只需要继续下沉,直至叶子结点。

因为是在子树中做交换,对树的其他分支并没有影响。

//下沉维护堆
	public static void sink(int[]a,int k,int N){
		
		
		
		while(2*k+1<=N){
			
			
			int j = 2*k+1;//左孩子的下标
			
			//j<N,说明存在右孩子
			//a[j]<a[j+1],左孩子小于右孩子
			if(j<N&&a[j]<a[j+1]) 
				j++;
			
			//如果根结点并没有小于他的孩子,不用下沉了
			if(!(a[k]<a[j]))
				break;
			
			//否则
			Example.exch(a, k, j);
			
			//交换后,更改当前,继续下沉
			k=j;
			
			
		}//end while
		
		
		
	}//end sink

 

二:下沉实现的堆排序(原地排序)

1、构建堆

对于一个普通的数组,我们可以将他看成一个无序的堆。

要使用堆排序,第一步就要将该数组构造成一个有序堆。

一个简单的思想是,创建一个新数组,通过堆的插入元素方法,一个一个插入原数组的元素,进而完成一个新堆。

其实并不需要,我们只要将前N/2的元素,下沉到合适的位置即可,此时堆即有序,后N/2全是叶子结点,无需下沉。

 

2、下沉排序

根据定义,根结点,既是数组的0位元素,永远是有序堆的最大值。

所以我们只需要将它和数组最后一个元素交换位置,再将大小为N-1的数组(除去最后一个元素,因为他是最大值,放在最后),从新构建成一个新堆即可。怎么构建呢?就是把新的0号元素下沉到适合的位置。

如此往复,即可得到一个有序的数组。

//下沉排序
	public static void sort(int []a){
		
		
		//构建堆
		//一开始可以将数组看作无序的堆
		//将从下标为N/2开始一直到0的元素下沉到合适的位置即可。N/2后面的元素,其实都是叶子结点,无需下沉。
		int N = a.length-1;
		
		for(int k = N/2;k>=0;k--){
			
			sink(a,k,N);
			
		}
		
		
		
		
		//下沉排序
		//堆的根结点永远是最大值,我们只需将最大值和最后一位的元素互换位置。然后再维护一个除原最大结点以外的N-1的堆,再将新堆的根节点放在倒数第二的位置。如此反复
		
		while(N>0){
			
			Example.exch(a, 0, N--);
			sink(a,0,N);
			
		}
		
		
	}//end sort

 

时间复杂度:

主要是下沉,其实下沉的时间复杂度非常直观,每次下沉,结点的高度-1,一棵树的高度是lgN,所以从0号位下沉到最后一位(最坏情况),也只是lgN。

后面我们在下沉排序里面对每个元素其实都进行了下沉,所以总的来说是NlgN。

 

空间复杂度:

我们在该算法中,并没有用到临时变量或者新建的数组,所以不需要额外的空间。复杂度是常数。

 

稳定性:

不稳定

 

以上是关于排序算法之堆排序(优先队列)的主要内容,如果未能解决你的问题,请参考以下文章

排序算法总结之堆排序

排序算法之堆排序

算法排序之堆排序

重温基础算法内部排序之堆排序法

重温基础算法内部排序之堆排序法

简易学算法之堆排序