堆排序虽然叫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