堆排序
堆排序时间复杂度为O(nlgn),且具有空间原址性,只需要常数个额外的元素空间存储临时数据。但堆排序是不稳定的算法,即对相等的两个元素,排序后不能保证他们的顺序与原始数据的顺序相同。
1.堆
堆是一个数组,可以把它看成一个近似的完全二叉树。树上的每一个结点对应数组中的一个元素,除最底层外,该树是完全充满的,而且是从左向右填充。
堆数组有两个和堆排序相关的重要属性,数据元素的个数(length)和堆元素(heapSize)。可以理解为数组中只有一部分数据用来表示当前堆,因此 0 <= heapSize <= length。
编程时,堆的根节点在数组中的索引值为0。对给定的某个节点的索引i,其父结点、左孩子和右孩子可以计算得出:
parent(i) = (i - 1) >> 1;
left(i) = (i << 1) + 1;
right(i) = (i << 1) + 2;
堆又分为最大堆和最小堆。在最大堆中,根节点的值不小于它的任意一个子节点,即最大元素存在根节点中。在最小堆中,根节点不大于它的任意一个子节点,即最小元素存在根节点中。
2.维护堆的性质
维护堆的性质是维护最大堆或最小堆的重要过程,维护最大堆或最小堆需要多次使用此方法。以最大堆为例,其算法如下:
maxHeapify(a, i, heapSize)
l = left(i);
r = right(i);
largest = i;
if l < heapSize and a[largest] < a[l]
largest = l;
if r < heapSize and a[largest] < a[r]
largest = r;
if largest != i
exchange(a[largest], a[i])
maxHeapify(a, largest, heapSize)
程序中a[i],a[l],a[r]中选出最大的,并将其下标存储在largest中。如果a[i]是最大的,那么以i为根的结点的子树已经是最大堆。否则,最大元素是i的某个子结点,则交换a[i]与a[largest]的值,从而使i及其孩子满足最大堆的性质。在交换后,下标为largest的结点的值是原来的a[i],于是以该结点为根的子树有可能违反最大堆的性质,需要对该子树递归调用maxHeapify方法。
3.建堆
建堆即建立最大堆或最小堆。需要用到maxHeapify方法。其算法如下
buildMaxHeap(a)
heapSize = a.length;
for i = (a.length >> 2) downto 0
maxHeapify(a, i, heapSize)
4.堆排序算法
(1)利用buildMaxHeap将输入的数组建立为最大堆
(2)因为最大堆中的第一个元素为最大值,因此可以把它与最后一个位置交换,这时候最大值即被排到最后
(3)但由于第一个元素改变,需要重新维护堆的性质,此时最后一个元素已排好序,去掉最后一个元素(heapSize减1)再次对第一个元素维护堆的性质即可
(4)反复执行过程(2)~(3),直到第二个元素,就完成了堆排序。
sort(a)
buildMaxHeap(a)
for i = a.length - 1 downto 1
exchange(a[0], a[i])
maxHeapify(a, 0, i)
java算法实现
package com.diysoul.algorithm.sort; import java.util.Random; public class HeapSort { public static void sort(int[] a) { buildMaxHeap(a); for (int i = a.length - 1; i > 0; i--) { int temp = a[0]; a[0] = a[i]; a[i] = temp; maxHeapify(a, 0, i); } } public static void buildMaxHeap(int[] a) { int heapSize = a.length; for (int i = a.length >>> 1; i >= 0; i--) { maxHeapify(a, i, heapSize); } } public static void maxHeapify(int[] a, int i, int heapSize) { int l = (i << 1) + 1; int r = (i << 1) + 2; int largest = i; if (l < heapSize && a[largest] < a[l]) { largest = l; } if (r < heapSize && a[largest] < a[r]) { largest = r; } if (largest != i) { int temp = a[i]; a[i] = a[largest]; a[largest] = temp; // print(a); maxHeapify(a, largest, heapSize); } } public static void main(String[] args) { int maxSize = 1000; int min = 0; int max = 1000; Random random = new Random(); int[] a = new int[maxSize]; for (int i = 0; i < a.length; i++) { a[i] = random.nextInt(max - min) + min; } print(a); sort(a); print(a); checkSort(a); } private static void checkSort(int[] array) { if (array == null || array.length == 0) return; for (int i = 1; i < array.length; i++) { if (array[i - 1] > array[i]) { System.out.println("Error! Array is not sorted in index " + i); break; } } } private static void print(int[] array) { if (array == null || array.length == 0) return; System.out.print("heap sort array(" + array.length + "): "); int i = 0; for (; i < array.length - 1; i++) { System.out.print(array[i] + ", "); } System.out.println(array[i]); } }