堆的排序分为两个阶段。在堆的构造阶段中,我们将原始数组重新组织排进一个堆中;然后在下沉排序阶段,我们从堆中取出所有的元素并得到排序结果。
1.堆的构造
由N个给定的元素构造一个堆,只需从左至右遍历数组,用swim()(参见优先队列)保证扫描指针左侧的所有元素已经是一颗堆有序的完全树既可(时间复杂度O(NlgN))。更高效的方法是从右至左用sink()函数构造子堆,数组的每个位置都已经是一个子堆的根结点,sink()对这些子堆也适用,这样的话就能递归地建立起堆的秩序。
//堆排序
public class HeapSort
{
public static void sort(Comparable[] a)
{
int N = a.length;
for (int k = N/2; k >= 1; k--)
{
sink(a,k,N);
}
while(N>1)
{
exch(a,1,N--);
sink(a,1,N);
}
}
private static void sink(Comparable[] a, int k, int N)
{
while(k*2 <= N)
{
int j = 2*k;
if (j < N && less(a,j,j+1)) j++;
if (!less(a,k,j)) break;
exch(a,k,j);
k = j;
}
}
private static void exch(Comparable[] a, int i, int j)
{
//本来是a[i]和a[j],数组用的是a[1]到a[N],而实际上大小为N的数组的表示是a[0]到a[N-1]
//所以i跟j相应地减1
Comparable temp = a[i-1];
a[i-1] = a[j-1];
a[j-1] = temp;
}
private static boolean less(Comparable[] a, int i,int j)
{
//理由同上
return (a[i-1].compareTo(a[j-1])) < 0;
}
}