排序算法(堆排序)

一、基本思路

堆排序的规则在于构造最小堆或最大堆,其核心是对堆的调整(adjust),以最大堆为例,我们需要将待排序数组看成一个完全二叉树,它有以下特点,每个节点都大于它的任何子节点,符合这个条件的堆,就是最大堆,如

其对应的数组为 { 9,8,7,6,5,4,3,2 }

当我们拿到一个数组时,我们的最开始需要利用待排序区间构建一个最大堆,然后将堆顶的数,也就是当前待排序区间最大的数移到待排序区间最后一个(与最后一个交换),并把待排序区间缩减1,然后逐步调整根节点

如何构建初始最大堆(其中需要多步调整)是我们整个排序最核心的部分,其大致的原理为

1.从最后一个非叶子节点开始调整,如上图值为6的节点,以它作为子树,对它进行调整

调整方法为:将子树根节点保存为一个temp值逐层往下探索,每次遇到比该temp值大的子节点,将两个子节点最大的值赋给它们的父节点,并往最大子节点的那个分支继续探索,如果遇到的该层的子节点均比该temp值小,那么我们就找到了temp值应该在的位置啦,把temp放进去,这样一来,该子树也就调整为局部最大堆了

2.调整倒数第二个、倒数第三个非叶子节点......

3.当我们调整完最后的非叶子节点,也就是堆顶的节点时,最大堆也就构建完成了

4.此时,将堆顶的元素放入已排序区(与最后一个元素交换),待排序区间缩减1

二、例子

我们以{ 0,1,2,3,6,5,4,7,9,8 }为例

第一步:构建初始最大堆

1.调整值为 6 的这个节点代表的子树,temp值为6,我们可以看到,它的子节点中最大的为8,比temp大,把8赋给父节点,如下

下面的这个8形同虚设,你可以将它理解为只是一个即将被填充的坑而已

此时,往下寻找temp值应该在的地方,由于已经到底了,所以我们将temp填进这个坑,如下

经过一次调整后的树为

2.调整值为3的这个节点代表的子树,temp值为3,往下找,找到最大子节点9,并且还大于temp,9赋给父节点,由于已经到最底,将temp填进这个坑,得到的树为

3.调整值为2的这个节点代表的子树,temp值为2,与上面类似,得到的树为

4.调整值为1的这个节点代表的子树,此时temp值为1,可以预想到,1这么小肯定会被放到最底下啦,我们来看看吧

往下第一层,取最大的子节点9,由于9比temp大,  将9赋给父节点,留下一个坑,如

再往下看,取最大的子节点7,由于7比temp大,  将7赋给父节点那个坑,如

再把temp值填进这个坑,得到如下

5.调整值为0的节点,按照前面的调整方法,我们可以得到初始的最大堆为

第二步:逐步调整根节点

将堆顶的数,也就是当前待排序区间最大的数移到待排序区间最后一个(与最后一个交换),并把待排序区间缩减1,(如下图),然后调整根节点,也就是值为0的那个节点

重复第二步,直到待排序区间为0,就大功告成啦

三、代码实现

public class HeapSort {//以最大堆为例

	public static void main(String[] args) {
		int [] a=new int[]{3,5,7,9,8,6,2,1,4,0};
		HeapSort heaptSort=new HeapSort();
		heaptSort.heapSort(a);
		for(int item:a){
			System.out.print(item+" ");
		}

	}
	private void heapSort(int[] a){
		for(int i=a.length/2-1;i>=0;i--){//先构建初始最大堆
			adjustHeap(a, i, a.length);
		}
		for(int j=a.length-1;j>0;j--){//交换+调整根节点
			int t=a[j];
			a[j]=a[0];
			a[0]=t;
			adjustHeap(a, 0, j);
		}
	}
	private void adjustHeap(int[] a,int i,int length){//i为非叶子节点的位置,length为堆的长度
		int temp=a[i]; //此次要调整的子树的根节点的值
		for(int k=2*i+1;k<length;k=2*k+1){ //一层一层观察子节点能否
			if(k+1<length&&a[k]<a[k+1]){ 
				k++;    //如果左节点比右节点小,k指向右节点,这保证了k一直指向最大的子节点
			}
			if(a[k]>temp){
				a[i]=a[k];   //如果最大子节点比根节点还大,则把最大子节点的值赋给父节点,不作交换
				i=k;
			}
			else
				break;
		}
		a[i]=temp;//上面循环过后,i已经指向了一个其子节点都小于temp的节点,将temp填进该位置
	}

}

四、复杂度

最佳、平均、最差情况均为O(nlogn)

猜你喜欢

转载自blog.csdn.net/andy_budd/article/details/81078378