基本思想
1)将待排序序列构造成一个大顶堆
2)此时,整个序列的最大值就是堆顶的根节点。
3)将其与末尾元素进行交换,此时末尾就为最大值。
4)然后将剩余n-1 个元素重新构造成-一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
初始堆
构建第一个大顶堆
将序列的最大值即对顶的根节点与末尾元素进行交换,此时末尾就为最大值。最大值就已经排序出来了,如此反复循环可以得到升序序列。
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int[] a = {35, 63, 48, 9, 86, 24, 53, 11};
int[] nums = heapsort(a);
System.out.println(Arrays.toString(nums));
}
public static int [] heapsort(int []a){
int len=a.length;
if(len<=1)return a;
bulidHeap(a);
while(len>0){
swap(a,0,len-1);
len--;
adjustHeap(a,0,len);
}
return a;
}
/*
我一开始看到这这个代码的时候主要有这个疑惑,为什么第一次构建大顶堆的时候
需要从最后一个分支节点来进行循环调用adjustheap函数直到根节点,而在第一
次构建完大顶堆后,此后每次构建大顶堆时只需要调用一次adjust函数,我把我
具体的理解放在了文章下面。
*/
public static void bulidHeap(int []a){
for(int i=(a.length/2)-1;i>=0;i--){
adjustHeap(a,i,a.length);
}
}
public static void adjustHeap(int []a,int i,int len){
int maxIndex=i;
int left=2*i+1;
int right=2*i+2;
if(left<len&&a[left]>a[maxIndex])
maxIndex=left;
if(right<len&&a[right]>a[maxIndex])
maxIndex=right;
if(maxIndex!=i){
swap(a,maxIndex,i);
adjustHeap(a,maxIndex,len);
}
}
public static void swap(int []a,int i,int j){
int temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}
第一次构建大顶堆的过程在上面已经用图表示的很清楚了,buildHeap函数里面的循环意思就是从最后一个分支节点开始向上遍历每一个分支节点,把他们都构造成大顶堆,在adjustHeap函数里面的if(maxIndex!=I)判断语句要是为真,if判断语句之后的执行语句执行调整的节点是当前节点的子节点,注意这是重点。
如果没有构建好大顶堆,那么节点的大小顺序按照层次来看是没有规律的,如果直接调用adjustHeap函数并且传递的是第一个节点的参数的话,很可能出现以下的情况
由图我们可以看出来这个是构建不出来一个正确的大顶堆的,因为我写的这里的adjustHeap函数它内部的语句
if(maxIndex!=i){
swap(a,maxIndex,i);
adjustHeap(a,maxIndex,len);
}
是向下调整的。
在第一次构建好大顶堆后,节点的大小顺序是第一层大于第二层依次类推的,所以在第一次构建好大顶堆并且将其首元素与末尾元素进行交换后直接调用adjustHeap函数并且传参传的是堆顶元素的索引,adjustHeap里面的
if(maxIndex!=i){
swap(a,maxIndex,i);
adjustHeap(a,maxIndex,len);
}
语句会让它会自己向下调整构建大顶堆。