堆排序(heapsort)
堆排序的运行时间为Θ(nlgn),它是一种原地排序算法:在任何时候,数组中只有常数个元素存储在输入数组以外;
对数据结构是一种数组对象,它可以被视为一棵完全二叉树。树中得每个结点与数组中存放该结点值的那个元素对应。
树的每一层都是填满的,最后一层可能除外,表示堆的数组A是一个具有两个属性的对象:length[A]是数组中的元素
个数,heap-size[A]是存放A中的堆元素个数。
给定某个结点的下标 i , 其父结点PARENT(i),左儿子LEFT(i)和右儿子RIGHT(i)的下标可以计算出来:
PARENT(i)
return i/2
LEFT(i)
return 2 * i
RIGHT(i)
return 2 * i + 1
最大堆是指除了根以外的每个结点 i,有 A[PARENT(i)] >= A[i]
最小堆是指除了根以外的每个结点 i,有 A[PARENT(i)] <= A[i]
在堆排序算法中使用的是最大堆。
保持堆的性质 MAX- HEAPIFY 过程:
MAX-HEAPIFY(A, i) l ← LEFT(i) r ← RIGHT(i) if l <= heap-size[A] and A[l] > A[i] then largest ← l else largest ← i if r <= heap-size[A] and A[r] > A[largest] then largest ← r if largest != i then exchange A[i] ↔ A[largest] MAX-HEAPIFY(A, largest)
BUILD-MAX-HEAP过程:
BUILD-MAX-HEAP(A) heap-size(A) ← length[A] for i ← length[A]/2 downto 1 do MAX-HEAPIFY(A, i)
堆排序算法:
HEAPSORT(A) BUILD-MAX-HEAP(A) for i ← length[A] downto 2 do exchange A[1] ↔ A[i] heap-size[A] ← heap-size[A] - 1 MAX-HEAPIFY(A, 1)
Java实现
public void heapSort(int[] a) { int heapSize = a.length; buildMaxHeap(a); for (int i = a.length - 1; i > 0; i--) { swap(a, 0, i); maxHeapify(a, 0, --heapSize); } } private void buildMaxHeap(int[] a) { for (int i = a.length / 2; i >= 0; i--) { maxHeapify(a, i, a.length); } } private void maxHeapify(int[] a, int i, int heapSize) { int largest = 0; int l = 2 * i + 1; int r = 2 * i + 2; if (l < heapSize && a[l] > a[i]) { largest = l; } else { largest = i; } if (r < heapSize && a[r] > a[largest]) { largest = r; } if (largest != i) { swap(a, i, largest); maxHeapify(a, largest, heapSize); } } private void swap(int[] data, int src, int des) { int tmp = data[des]; data[des] = data[src]; data[src] = tmp; }
用Java实现伪码需要注意
1.数组的下标:
- 大于1还是大于0,等等
- 因为Java的数组下标是从0开始,与伪码对应的结点 i 左孩子右孩子的下标分别是2*1+1, 2*i+2.
2.maxHeapify需要一参数heapSize是因为在heapSort交换后,要从堆中去掉此元素结点(此时去掉的元素结点就是堆中的最大结点)
3.堆排序是分3个过程来完成,在最后验证堆排序的实现是否正确,先测试验证maxHeapify正确,然后是建堆buildMaxHeap正确,最后验证
堆排序是否正确。否则一开始就验证堆排序,出了问题就不知道到底是哪个过程出错。