通过阅读读英文版的《Algorithms》,弄懂了HeapSort。下面将自己对HeapSort的理解过程记录下来。 (为了简洁,使用术语“heap”代表“binary heap”)
定义: 二叉树堆(a binary heap) 是一组键的集合,按堆顺序排列在一个完整的二进制树中,按数组的级别顺序表示(不使用第一个条目)。
一、理解用数组表示的二叉树
数组表示的二叉树有两个非常简单、重要的定义:
一个大小为N的数组用完全二叉树表示,它的高为:lgN
l g N = = l o g 2 N lgN==log_{2}N lgN==log2N
在堆中,父节点的角标为【k/2】,相应的,父节点的两个子节点的角标分别为【2k】和【2k+1】。
二、理解堆排序的sink() 方法
/**
* a 数组;
* k a的角标;
* N 为a的最大角标
*/
private void sink(Comparable[] a, int k, int N){
while(k*2<=N){
int j= k*2;
// 获取较大值的角标
if(j<N && less(a[j-1],a[j])) j++;
if(!less(a[k-1], a[j-1])) break;
// 将较大值设置为父节点
exch(a, k-1, j-1);
k=j;
}
}
三、理解 堆排序
该排序算法很明显的一个特点是:原地排序。
该排序算法的第二部分有点像选择排序,但它使用了非常少的的比较。
heapsort 的排序方法在时间和空间上都是最优的。它保证了在最坏的情况下使用最多2NlgN次比较和固定的空间。
但这种排序方法很少被使用,因为它比较差的缓存特性,数组项很少和临近的项进行比较。
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, 0, --N);
sink(a, 1, N);
}
}