算法(第4版)-2.3 快速排序
public class Quick { public static void sort(Comparable[] a) { StdRandom.shuffle(a); // 消除对输入的依赖 sort(a, 0, a.length - 1); } private static void sort(Comparable[] a, int lo, int hi) { if (hi <= lo) return; int j = partition(a, lo, hi); // 切分 sort(a, lo, j - 1); // 将左半部分a[lo..j-1]排序 sort(a, j + 1, hi); // 将右半部分a[j+1..hi]排序 } private static int partition(Comparable[] a, int lo, int hi) { // 将数组切分为a[lo..i-1],a[i],a[i+1..hi] int i = lo, j = hi + 1; // 左右扫描指针 Comparable v = a[lo]; // 切分元素 while (true) { // 扫描左右,检查扫描是否结束并交换元素 while (less(a[++i], v)) if (i == hi) break; while (less(v, a[--j])) if (j == lo) break; if (i >= j) break; exch(a, i, j); } exch(a, lo, j); // 将v = a[j]放入正确的位置 return j; // a[lo..j-1] <= a[j] <= a[j+1..hi]达成 } 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); } }
主要优点:
· 实现简单
· 适用于各种不同的输入数据
· 在一般应用中比其他排序算法都要快得多
主要缺点:
· 非常脆弱
2.3.1 基本算法
1. 我们就是通过递归的调用切分来排序的。
2. 需要注意以下几点:
· 原地切分
· 别越界
· 保持随机性
· 终止循环
· 处理切分元素值有重复的情况
· 终止递归
2.3.2 性能特点
1. 快速排序的最好情况是每次都正好能将数组对半分。
2. 将长度为N的无重复数组排序,快速排序平均需要~2NlnN次比较(以及1/6的交换)。
3. 快速排序最多需要约n^2/2次比较,但随机打乱数组能够预防这种情况。
2.3.3 算法改进
1. 切换到插入排序
即小型数组由插入排序来完成。
2. 三取样切分
3. 熵最优的排序
-> 三向切分的快速排序
public class Quick3way { public static void sort(Comparable[] a) { StdRandom.shuffle(a); // 消除对输入的依赖 sort(a, 0, a.length - 1); } private static void sort(Comparable[] a, int lo, int hi) { if (hi <= lo) return; int lt = lo, i = lo + 1, gt = hi; Comparable v = a[lo]; while (i <= gt) { int cmp = a[i].compareTo(v); if (cmp < 0) exch(a, lt++, i++); else if (cmp > 0) exch(a, i, gt--); else i++; } // 现在 a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi]成立 sort(a, lo, lt - 1); sort(a, gt + 1, hi); } 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); } }
· 对于存在大量重复元素的数组,这种方法比标准的快速排序的效率高得多。