前言

排序算法的成本模型计算的是比较和交换的次数。less()方法对元素进行比较,exch()方法将元素交换位置。

1
2
3
4
5
6
7
8
9
private static boolean less(Comparable v, Comparable w) {
return (v.compareTo(w) < 0);
}

private static void exch(Comparable[] a, int i, int j) {
Comparable swap = a[i];
a[i] = a[j];
a[j] = swap;
}

堆的定义

堆的某个节点的值总是大于等于子节点的值,并且堆是一颗完全二叉树。当这棵树的每个结点都大于等于它的两个子节点时,它被称为堆有序。

堆可以用数组来表示,这是因为堆是完全二叉树,而完全二叉树很容易就存储在数组中。位置 k 的节点的父节点位置为 k/2,而它的两个子节点的位置分别为 2k 和 2k+1。

堆

上浮

在堆中,当一个节点比父节点大,那么需要交换这个两个节点。交换后还可能比它新的父节点大,因此需要不断地进行比较和交换操作,把这种操作称为上浮。

上浮

实现如下:

1
2
3
4
5
6
private void swim(int k) {
while (k > 1 && less(k/2, k)) {
exch(k, k/2);
k = k/2;
}
}

下沉

在堆中,当一个节点比子节点小,需要不断地向下进行比较和交换操作,把这种操作称为下沉。一个节点如果有两个子节点,应当与两个子节点中最大那个节点进行交换。

下沉

实现如下:

1
2
3
4
5
6
7
8
9
private void sink(int k) {
while (2*k <= N) {
int j = 2*k;
if (j < N && less(j, j+1)) j++;
if (!less(k, j)) break;
exch(k, j);
k = j;
}
}

堆排序

堆排序可以分为两个阶段。在堆的构造阶段中,我们将原始数组重新组织安排进一个堆中;然后在下沉排序阶段,我们从堆中按递减顺序取出所有元素并得到排序结果。

堆排序

堆的构造

无序数组建立堆最直接的方法是从左到右遍历数组进行上浮操作。一个更高效的方法是从右至左进行下沉操作,如果一个节点的两个节点都已经是堆有序,那么进行下沉操作可以使得这个节点为根节点的堆有序。叶子节点不需要进行下沉操作,可以忽略叶子节点的元素,因此只需要遍历一半的元素即可。

下沉排序

堆排序的主要工作都是在这一阶段完成的。这里我们将堆中的最大元素删除,然后放入堆缩小后数组中空出的位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Heap {
public static void sort(Comparable[] pq) {
int n = pq.length;
for (int k = n/2; k >= 1; k--)
sink(pq, k, n);
while (n > 1) {
exch(pq, 1, n--);
sink(pq, 1, n);
}
}

private static void sink(Comparable[] pq, int k, int n) {
while (2*k <= n) {
int j = 2*k;
if (j < n && less(pq, j, j+1)) j++;
if (!less(pq, k, j)) break;
exch(pq, k, j);
k = j;
}
}

private static boolean less(Comparable[] pq, int i, int j) {
return pq[i-1].compareTo(pq[j-1]) < 0;
}

private static void exch(Object[] pq, int i, int j) {
Object swap = pq[i-1];
pq[i-1] = pq[j-1];
pq[j-1] = swap;
}
}

复杂度分析

一个堆的高度为 logN,因此在堆中插入元素和删除最大元素的复杂度都为 logN。

对于堆排序,由于要对 N 个节点进行下沉操作,因此复杂度为 NlogN。

现代系统的许多应用很少使用它,因为它无法利用缓存。数组元素很少和相邻的其它元素进行比较,因此无法利用局部性原理,缓存未命中的次数很高。

  • 最坏时间复杂度 О(nlogn)
  • 最优时间复杂度 O(nlogn)
  • 平均时间复杂度 O(nlogn)
  • 空间复杂度 O(1)
  • 不稳定

参考资料