堆排序虽然叫heap sort,但是和内存上的那个heap并没有实际关系。算法上,堆排序一般使用数组的形式来实现,即binary heap。

我们可以将堆排序所使用的堆int[] heap视为一个完全二叉树,即,除非最后一层右侧有空结点,其他都为满结点。

对于任意heap[i]有如下一些性质:

1. i从1开始。

2. heap[i]的父节点为heap[i / 2]。

3. heap[i]的左子节点为heap[i * 2],右子节点为heap[i * 2 + 1]。

我们假设这个堆是一个最大堆,也就是父节点大于所有子节点,而且每个子树也有这样的性质。下面几个和堆排序有关的方法,基本来自于《算法导论》中的思路。

maxifyHeap方法,堆中index这个位置的结点破坏了整个堆的性质,但是其他结点的位置就是对的。我们首先将heap[index]和它左右子节点中,较大的那个交换位置,比如是left,然后递归调用maxifyHeap(left)。为什么假设只有index这个结点位置不对?因为这个方法一般是在建堆之后调用,也就是基于heap已经是最大堆的事实。这个方法的时间复杂度为O(logn)。

buildMaxHeap方法,建堆。也就是在堆在初始状态,很乱的时候,首次调用。思路是:从树的倒数第二层的最后一个有子节点的结点开始,递归调用上面的maxifyHeap方法。注意,开始的结点必然是heapLength,终点是1。时间复杂度为O(n)。

heapSort方法,首先用buildMaxHeap建堆,然后将堆顶元素和最后一个元素交换位置,这时候最后一个元素肯定到位了,最大。但是第一个元素交换过来的肯定不该在第一位,注意,这时其他结点仍然没有破坏最大堆的性质。所以我们只要调用maxifyHeap(1)就可以了。所以我们,循环这样做,每次将堆顶元素和最后一个元素交换位置,同时heapLength--。直到heapLength==1。时间复杂度为O(nlogn)。

public class BinaryHeap {
static int heapLength; public static void maxifyHeap(int[] heap, int index) {
if(index > heapLength) {
return;
}
int left = index * 2, right = index * 2 + 1;
int larger = -1;
if(left <= heapLength) {
if(heap[left] > heap[index]) {
larger = left;
}
}
if(right <= heapLength) {
if(heap[right] > heap[index] && heap[right] > heap[left]) {
larger = right;
}
}
swap(heap, index, larger);
maxifyHeap(heap, larger);
} public static void buildMaxHeap(int[] heap) {
for(int i = heapLength / 2; i >= 1; i--) {
maxifyHeap(heap, i);
}
} public void heapSort(int[] heap) {
buildMaxHeap(heap);
for(int i = heapLength; i >= 1; i--) {
swap(heap, 1, i);
heapLength--;
maxifyHeap(heap, 1);
}
} public static void swap(int[] heap, int index1, int index2) {
int temp = heap[index1];
heap[index1] = heap[index2];
heap[index2] = temp;
}
}

参考http://www.cnblogs.com/yangecnu/p/Introduce-Priority-Queue-And-Heap-Sort.html

04-28 04:10