堆:任意节点的值大于(小于)左右子节点的值,子节点也为堆。
如果对于存储在数组里的堆的话, 节点下标为 j, 它左子节点下标为 2*j+1, 右子节点下标为 2*j+2;
堆排序(升序):
调整堆:如果节点的左子节点与右子节点都已经是大根堆,调节该节点也为大根堆.
如果该节点的左右子节点的值都小于该节点,结束。(已经是大根堆)
选定一个最大的子节点 j,将当前根节点的值与最大子节点 p 的值交换,这样,a[p]>a[j],a[p]>a[j+1], 这里符合堆
但是将之前 a[p] 的值往下移动后,这个节点不一定是堆(但是它原来的左右节点已经是堆),因此继续调整 a[p] 为堆。
选择:
从 j=n*2-1 开始调整(从第二层最右边的节点开始),这样一层一层的调整,最后,最根节点的值为最大的值。
每次将最大的值与堆后面的值交换,然后减小堆的大小,重新调整最根节点为堆,重复这个步骤
public class HeapSort {
public static void adjust(int a[],int n,int p){
for(int j=2*p+1;j<n;j=2*p+1){
if(j+1<n&&a[j]<a[j+1])
j=j+1;//j 为大的子节点下标
if(a[j]<a[p])// 如果大的子节点比父节点小,结束(父节点已经为堆)
break;
int t=a[p];//根节点比子节点小,交换位置,导致新的子节点不为堆,继续调整子节点
a[p]=a[j];
a[j]=t;
p=j;//需要继续调整的子节点
}
}
public static void adjust2(int a[],int n,int p){
int t=a[p];//相比于上面,因为每次 t 都是保存最开始要调节的那个节点的值,只是不断往下移动位置
//可以省略步骤
for(int j=2*p+1;j<n;j=2*p+1){
if(j+1<n&&a[j]<a[j+1])
j++;
if(a[j]<t)
break;
a[p]=a[j];
p=j;
}
a[p]=t;
}
public static void heapSort(int a[],int n){
for(int i=n/2-1;i>=0;i--)
adjust(a,n,i);
for(int i=n-1;i>0;i--){
int t=a[0];
a[0]=a[i];
a[i]=t;
adjust2(a,i,0);
}
}
public static void main(String[]args){
int a[]=new int[100];
SortUtil.randomArray(a);
SortUtil.print(a);
heapSort(a,a.length);
SortUtil.print(a);
}
}