前言

排序算法的成本模型计算的是比较和交换的次数。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;
}

选择排序

首先找到数组中最小的那个元素,其次将它和数组的第一个元素交换位置。再次,在剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。如此反复,直到将整个数组排序。

选择排序
上图为选择排序的示例动画。红色表示当前最小值,黄色表示已排序序列,蓝色表示当前位置。

特点

  • 运行时间和输入无关:一个已经有序的数组或主键全部相等的数组和一个元素随机排列的数组所用的排序时间一样长。
  • 数据移动是最少的:每次交换都会改变两个数组的元素的值,因此选择排序用了N次交换。

复杂度分析

比较次数与关键字的初始状态无关,总的比较次数N = (n - 1) + (n - 2) +...+ 1 = n × (n - 1 ) / 2。交换次数最好情况是已经有序,交换0次;最坏情况是逆序,交换n-1次。

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

实现

1
2
3
4
5
6
7
8
9
10
11
12
public class Selection {
public static void sort(Comparable[] a) {
int n = a.length;
for (int i = 0; i < n; i++) {
int min = i;
for (int j = i+1; j < n; j++) {
if (less(a[j], a[min])) min = j;
}
exch(a, i, min);
}
}
}

插入排序

插入排序是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

插入排序

特点

  • 插入排序所需时间取决于输入中元素的初始顺序。
  • 插入排序对于部分有序的数组十分高效。

复杂度分析

最好情况是序列已经是升序排列了,在这种情况下,需要进行的比较操作需n-1次即可,不需要进行交换;最坏情况是降序排列,那么此时需要进行的比较共有n × (n - 1) / 2次,交换同样需要n × (n - 1) / 2次。

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

实现

1
2
3
4
5
6
7
8
9
10
public class Insertion {
public static void sort(Comparable[] a) {
int n = a.length;
for (int i = 1; i < n; i++) {
for (int j = i; j > 0 && less(a[j], a[j-1]); j--) {
exch(a, j, j-1);
}
}
}
}

希尔排序

希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序的思想是使数组中任意间隔为h的元素都是有序的,这样的数组称为h有序数组。

实现希尔排序只需要在插入排序的代码中将移动元素的距离由1改为h即可。

希尔排序
一个h有序数组即一个由h个有序子数组组成的数组

希尔排序
上图表示以23, 10, 4, 1的步长序列进行希尔排序。

特点

  • 希尔排序的时间复杂度与递增序列密切相关,所以分析希尔排序的时间复杂度是个比较麻烦的事。
  • 希尔排序对于中等大小规模表现良好,对规模非常大的数据排序不是最优选择。
  • 希尔排序实现简单,几乎任何排序工作在开始时都可以用希尔排序,若在实际使用中证明它不够快,再改成快速排序这样更高级的排序算法。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Shell {
public static void sort(Comparable[] a) {
int n = a.length;

// 3x+1 increment sequence: 1, 4, 13, 40, 121, 364, 1093, ...
int h = 1;
while (h < n/3) h = 3*h + 1;

while (h >= 1) {
// h-sort the array
for (int i = h; i < n; i++) {
for (int j = i; j >= h && less(a[j], a[j-h]); j -= h) {
exch(a, j, j-h);
}
}
h /= 3;
}
}
}

参考资料