BruceFan's Blog

Stay hungry, stay foolish

0%

算法-排序

运行时间
评估算法的性能。首先,要计算各个排序算法在不同的随机输入下的基本操作的次数(包括比较和交换,或者是读写数组的次数)。然后,我们用这些数据估计算法的相对性能。
额外的内存使用
排序算法的额外内存开销和运行时间是同样重要的。排序算法可分为两类:除了函数调用所需的栈和固定数目的实例变量之外无需额外内存的原地排序算法,以及需要额外内存空间来存储另一份数组副本的其他排序算法。

排序算法类模板

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
32
33
34
public class Example {
public static void sort(Comparable[] a) {

}

private static boolean less(Comparable v, Comparable w) {
return v.compareTo(w) < 0;
}

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

private static void show(Comparable[] a) {
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
System.out.println();
}

public static boolean isSorted(Comparable[] a) {
// 测试元素是否有序
for (int i = 0; i < a.length; i++) {
if (less(a[i], a[i-1])) return false;
}
return true;
}

public static void main(String[] args) {
String[] a = {"S", "O", "R", "T", "E", "X", "A", "M", "P", "L", "E"};
sort(a);
show(a);
}
}

这个模板适用于任何实现了Comparable接口的数据类型。很多希望排序的数据都实现了Comparable接口。如Java中封装数字的类型Integer和Double,以及String和其他许多高级数据类型都实现了Comparable接口。在创建自己的数据类型时,只要实现Comparable接口就能够保证用例代码可以将其排序。

选择排序

  1. 找到数组中最小的元素,将它和数组的第一个元素交换位置。
  2. 在剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。
  3. 如此往复,直至整个数组排序。

两个特点

  • 运行时间和输入无关。
  • 数据移动是最少的。
1
2
3
4
5
6
7
8
9
10
public static void selectionSort(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);
}
}
}

插入排序

将每一个元素插入到其他已经有序元素中的适当位置,为了给要插入的元素腾出空间,需要将其余所有元素在插入之前都向右移动一位。
对于某些类型的非随机数组很有效。

1
2
3
4
5
6
7
8
public static void insertionSort(Comparable[] a) {
// 将a按升序排列
int n = a.length;
for (int i = 1; i < n; i++) { // 将a[i]插入到a[i-1]、a[i-2]、a[i-3]...之中
for (int j = i; j > 0 && less(a[j], a[j-1]); j--)
exch(a, j, j - 1);
}
}

希尔排序

希尔排序的思想是使数组中任意间隔为h的元素都是有序的。这样的数组被称为h有序数组。一个h有序数组就是h个互相独立的有序数组编织在一起组成的一个数组。
实现方法:因为子数组是相互独立的,一种简单的方法是在h子数组中将每个元素交换到比它大的元素之前去。只需要在插入排序的代码中将移动元素的距离由1改为h即可。这样,希尔排序的实现就转化为了一个类似于插入排序但使用不同增量的过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void shellSort(Comparable[] a) {
// 将a[]按升序排列
int N = a.length;
int h = 1;
while (h < N/3) h = 3 * h + 1; // 1, 4, 13, 40, 121...
while (h >= 1) { // 将数组变为h有序
for (int i = h; i < N; i++) { // 将a[i]插入到a[i-h],a[i-2*h],a[i-3*h]...之中
for (int j = i; j >= h && less(a[j], a[j-h]); j -= h)
exch(a, j, j-h);
}
h = h / 3;
}
}