# 一. 写在前面

## 1.2 排序算法的抽象设计

``````public class Date implements Comparable<Date> {
/* ... */
public int compareTo(Date that) {
if (this.year < that.year) return -1;
if (this.year > that.year) return +1;
if (this.month < that.month) return -1;
if (this.month > that.month) return +1;
if (this.day < that.day) return -1;
if (this.day > that.day) return +1;
return 0;
}
/* ... */
}
``````

``````private static boolean less(Comparable v, Comparable w) {
return (v.compareTo(w) < 0);
}
private static void exch(int[] a, int i, int j) {
int swap = a[i];
a[i] = a[j];
a[j] = swap;
}
``````

``````import java.util.Arrays;
import java.util.Comparator;
public class Transaction implements Comparable<Transaction> {
private final String  who;      // customer
private final Date    when;     // date
private final double  amount;   // amount
/* ... */
/**     * Compares two transactions by customer name.     */
public static class WhoOrder implements Comparator<Transaction> {
public int compare(Transaction v, Transaction w) {
return v.who.compareTo(w.who);
}
}

/**     * Compares two transactions by date.     */
public static class WhenOrder implements Comparator<Transaction> {
public int compare(Transaction v, Transaction w) {
return v.when.compareTo(w.when);
}
}

/**     * Compares two transactions by amount.     */
public static class HowMuchOrder implements Comparator<Transaction> {
public int compare(Transaction v, Transaction w) {
if      (v.amount < w.amount) return -1;
else if (v.amount > w.amount) return +1;
else                          return  0;
}
}

public static void main(String[] args) {
Transaction[] a = new Transaction[4];
a[0] = new Transaction("Turing   6/17/1990  644.08");
a[1] = new Transaction("Tarjan   3/26/2002 4121.85");
a[2] = new Transaction("Knuth    6/14/1999  288.34");
a[3] = new Transaction("Dijkstra 8/22/2007 2678.40");

StdOut.println("Unsorted");
for (int i = 0; i < a.length; i++)
StdOut.println(a[i]);
StdOut.println();

StdOut.println("Sort by date");
Arrays.sort(a, new Transaction.WhenOrder());
for (int i = 0; i < a.length; i++)
StdOut.println(a[i]);
StdOut.println();

StdOut.println("Sort by customer");
Arrays.sort(a, new Transaction.WhoOrder());
for (int i = 0; i < a.length; i++)
StdOut.println(a[i]);
StdOut.println();

StdOut.println("Sort by amount");
Arrays.sort(a, new Transaction.HowMuchOrder());
for (int i = 0; i < a.length; i++)
StdOut.println(a[i]);
StdOut.println();
}
}
``````

``````// is v < w ?
private static boolean less(Comparator c, Object v, Object w)  {
return (c.compare(v, w) < 0);
}
// exchange a[i] and a[j]
private static void exch(Object[] a, int i, int j) {
Object swap = a[i];
a[i] = a[j];
a[j] = swap;
}
``````

# 二. 基础排序算法

## 2.1 选择排序

``````public class Selection {
// This class should not be instantiated.
private 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);
assert isSorted(a, 0, i);
}
assert isSorted(a);
}
}
``````

## 2.2 插入排序

2-2 插入排序很像打扑克
``````public class Insertion {
// This class should not be instantiated.
private Insertion() { }
public static void sort(Comparable[] a) {
int N = a.length;
for (int i = 0; i < N; i++) {
for (int j = i; j > 0 && less(a[j], a[j-1]); j--) {
exch(a, j, j-1);
}
assert isSorted(a, 0, i);
}
assert isSorted(a);
}
}
``````

## 2.3 Shell排序

``````public class Shell {
// This class should not be instantiated.
private 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;
}
assert isSorted(a);
}
}
``````

## 2.4 随机洗牌算法

``````public static void shuffle(Object[] a) {
int N = a.length;
for (int i = 0; i < N; i++) {
int r = StdRandom.uniform(i + 1);
exch(a, i, r);
}
}
public static void shuffle(int[] a) {
int N = a.length;
for (int i = 0; i < N; i++) {
int r = i + uniform(N-i); // between i and N-1
int temp = a[i];
a[i] = a[r];
a[r] = temp;
}
}
``````

# 三. 算法名人堂——归并排序

## 3.1 排序算法的稳定性

“稳定性”是在对记录进行多种方式排序时通常要考虑的问题，如果记录可以通过Key1排序，又可以通过Key2排序，那么在Key2排序之后，如果Key2相同的一众记录相对于Key1而言仍然是有序的，那么我们就说该排序算法是稳定排序，否则，就是不稳定的，图3-1中所示的例子中，我们先对学生记录按照姓名进行排序，然后又按照分区进行排序，结果第3区的学生不再是按姓名排序的，因此选择排序并不是一个稳定的算法。在我们介绍过的算法中，只有插入排序是稳定的，因为它每次移动元素的跨度很小，不会跑到跟它一样大的元素前面去。而选择排序和Shell排序跨度都比较大，比如Shell排序一开始就每间隔h个元素进行排序，自然无法保证稳定性。

## 3.2 归并排序中的归并

``````private static void merge(Comparable[] a, Comparable[] aux, int lo, int mid, int hi) {

// precondition: a[lo .. mid] and a[mid+1 .. hi] are sorted subarrays
assert isSorted(a, lo, mid);
assert isSorted(a, mid+1, hi);

// copy to aux[]
for (int k = lo; k <= hi; k++) {
aux[k] = a[k];
}

// merge back to a[]
int i = lo, j = mid+1;
for (int k = lo; k <= hi; k++) {
if      (i > mid)              a[k] = aux[j++];   // this copying is unnecessary
else if (j > hi)               a[k] = aux[i++];
else if (less(aux[j], aux[i])) a[k] = aux[j++];
else                           a[k] = aux[i++];
}

// postcondition: a[lo .. hi] is sorted
assert isSorted(a, lo, hi);
}
``````

## 3.3 各个击破：自顶向下的归并

``````// mergesort a[lo..hi] using auxiliary array aux[lo..hi]
private static void sort(Comparable[] a, Comparable[] aux, int lo, int hi) {
if (hi <= lo) return;
int mid = lo + (hi - lo) / 2;
sort(a, aux, lo, mid);
sort(a, aux, mid + 1, hi);
merge(a, aux, lo, mid, hi);
}
``````
3-4 归并与插入排序的效率对比
3-5 归并排序的递推公式
3-6 归并算法效率的直观解释

## 3.4 积少成多：自底向上的归并

``````public static void sort(Comparable[] a) {
int N = a.length;
Comparable[] aux = new Comparable[N];
for (int sz = 1; sz < N; sz = sz+sz) {
for (int i = 0; i < N-sz; i += sz+sz) {
int lo = i;
int m  = i+sz-1;
int hi = Math.min(i+sz+sz-1, N-1);
merge(a, aux, lo, m, hi);
}
}
assert isSorted(a);
}
``````

## 3.5 基于比较的排序算法，其极限何在？

### 推荐阅读更多精彩内容

• 概述 排序有内部排序和外部排序，内部排序是数据记录在内存中进行排序，而外部排序是因排序的数据很大，一次不能容纳全部...
蚁前阅读 4,332评论 0 52
• 概述：排序有内部排序和外部排序，内部排序是数据记录在内存中进行排序，而外部排序是因排序的数据很大，一次不能容纳全部...
每天刷两次牙阅读 3,011评论 0 16
• 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 将一个记录插入到已排序好...
依依玖玥阅读 514评论 0 2
• 概述排序有内部排序和外部排序，内部排序是数据记录在内存中进行排序，而外部排序是因排序的数据很大，一次不能容纳全部的...
Luc_阅读 1,786评论 0 35
• 本篇有7k+字, 系统梳理了js中排序算法相关的知识, 希望您能喜欢. 原文:JS中可能用得到的全部的排序算法 导...
Java高级架构阅读 1,440评论 1 21