堆的定义以及堆排序的思想:
1)用数组存,第i
个节点的孩子分别是2i+1
和2i+2
.
2)根的值小于(或大于)左右子树,子树也满足这个定义
3)堆看起来是一棵完全二叉树,存储却只需要用到数组即可。
4)开始建堆的时候数组顺序与二叉树层次遍历对应,逐步从非叶子节点到根调整。构建二叉堆本质上是让所有非叶子节点“下沉”到自己该到的位置
5)堆排序以大根堆为例,每次堆顶(最大)和最后那个对调。
保证大的放到目前堆的最后。然后把顶部元素调整好,最后就是升序
程序代码:
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int[] a = {57,68,59,52,72,28,96,33,24};
heapSort(a);
System.out.println(Arrays.toString(a));
}
public static void heapSort(int[] a) {
for(int i=a.length/2-1;i>=0;i--) {//从倒数第一个非叶子节点开始调整,一直到调整到根为0,让数组满足大根堆的递归定义
//对数组的第i个元素当做根,调整范围是目前无序的空间
adjustHeap(a,i,a.length);
}
for(int i=a.length-1;i>0;i--) {
swap(a,0,i);//交换数组里根和无序空间的最后一个节点
adjustHeap(a, 0, i);//每一轮都把前i个里最大的放到最后,这样无序的范围都是0到i
}
}
public static void swap(int[] a,int root,int last) {
int tmp = a[root];
a[root] = a[last];
a[last] = tmp;
}
//左右孩子选大的和根进行交换,继续对左或右子树进行调整,保证整个堆递归满足大根堆的定义
public static void adjustHeap(int[] a,int i,int len) {
int bak=a[i];//备份一下要调整的元素
for(int j=2*i+1;j<len;j=2*j+1) {//开始的时候,从待调整元素i的左右子根开始2i+1,后面每次递归2j+1
if(j+1<len && a[j+1]>a[j]) j++;//让j永远都指向自己左右孩子中大的那一个
if(bak<a[j]) {
a[i]=a[j];//大的元素拷贝到i这个位置
i=j;//下一次从以j为根的子树开始,让i等于这个j作为根
} else {
break;
}
}
a[i]=bak;//放进去,一次性把最开始要调整的a[i]放到该放的位置
}
}