背景
本文主要讨论几个初级排序算法,用Java语言来实现。首先给出一个排序算法类模板Example类:将排序代码放在类的sort()方法中,该类还将包含辅助函数less()和exch()以及一个示例用例main()。less()方法对元素进行比较,exch()方法将元素交换位置。下面给出代码:
import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;
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++) {
StdOut.print(a[i] + " ");
StdOut.println();
}
}
// 测试数组元素是否有序
public static boolean isSorted(Comparable[] a) {
for (int i = 1; i < a.length; i++) {
if (less(a[i], a[i-1])) {
return false;
return true;
}
}
}
// 从标准输入读取字符串,将它们排序并输出
public static void main(String[] args) {
String[] a = In.readStrings();
sort(a);
assert isSorted(a);
show(a);
}
}
选择排序
首先,找到数组中最小的那个元素,其次,将它和数组的第一个元素交换位置(如果第一个元素就是最小元素那么它就和自己交换)。再次,在剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。如此往复,直到将整个数组排序。
它有两个鲜明的特点:
- 运行时间和输入无关。
- 数据移动是最少的。
下面给出代码实现:
public class Selection {
// 将a[]按升序排列
public static void sort(Comparable[] a) {
int N = a.length; // 数组长度
// 将a[i]和a[i+1..N]中最小的元素交换
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, j, min);
}
}
}
插入排序
插入排序即将元素插入到有序的元素中,在实现中,为了要给插入的元素腾出空间,需要将其余所有元素在插入之前都向右移动一位。
插入排序所需的时间取决于输入中元素的初始顺序。插入排序对于实际应用中常见的某些类型的非随机数组很有效。下面给出代码实现:
public class Insertion {
// 将a[]按升序排列
public static void sort(Comparable[] a) {
int N = a.length;
// 将a[i]插入到a[i-1]、a[i-2]、a[i-3]...之中
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有序数组。即一个h有序数组就是h个互相独立的有序数组编织在一起组成的一个数组。
希尔排序更高效的原因是它权衡了子数组的规模和有序性。排序之初,各个子数组都很短,排序之后子数组都是部分有序的,这两种情况都很适合插入排序。子数组部分有序的程度取决于递增序列的选择。代码如下:
public class Shell {
// 将a[]按升序排列
public static void sort(Comparable[] a) {
int N = a.length;
int h = 1;
while(h < N/3) {
h = 3 * h + 1;
}
// 将数组变为h有序
while(h >= 1) {
// 将a[i]插入到a[i-h],a[i-2*h],a[i-3*h]...之中
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 = h / 3;
}
}
}