Ex2_3_18三取样切分

  1. import edu.princeton.cs.algs4.In;
  2. import edu.princeton.cs.algs4.StdOut;
  3. import edu.princeton.cs.algs4.StdRandom;
  4. public class Ex2_3_18 {
  5. private static void sort(Comparable[] a){
  6. StdRandom.shuffle(a);
  7. sort(a,0,a.length-1);
  8. }
  9. private static void sort(Comparable[] a, int lo, int hi){
  10. if(lo <= hi + 5) { Insertion(a,lo,hi); return;}//以5为阈值进行插入排序
  11. int j = partition(a,lo,hi);
  12. sort(a,lo,j-1);
  13. sort(a,j+1,hi);
  14. }
  15. private static int partition(Comparable[] a, int lo, int hi){
  16. int i = lo;
  17. int j = hi + 1;
  18. while (true){
  19. while (less(a[i++],a[midNum(a,lo,hi)]));
  20. while (less(a[lo],a[j--]));
  21. if(i >= j) break;
  22. exch(a,i,j);
  23. }
  24. exch(a,lo,j);
  25. return j;
  26. }
  27. //三取样,以中位数为切分元素,并把切分元素移动到末端作为标记
  28. private static int midNum(Comparable[] a, int lo, int hi){
  29. if( (a[lo].compareTo(a[lo+1]) * a[lo].compareTo(a[lo+2])) < 0){
  30. exch(a,lo,a.length-1);
  31. return lo;
  32. }
  33. else if( (a[lo+1].compareTo(a[lo]) * a[lo+1].compareTo(a[lo+2])) < 0){
  34. exch(a,lo+1,a.length-1);
  35. return lo+1;
  36. }
  37. else{
  38. exch(a,lo+2,a.length-1);
  39. return lo+2;
  40. }
  41. }
  42. private static void Insertion(Comparable[] a, int lo, int hi){
  43. for(int i = lo+1; i <= hi; i++){
  44. for(int j = i; j>lo && less(a[j],a[j-1]); j--){
  45. exch(a,j,j-1);
  46. }
  47. }
  48. }
  49. private static boolean less(Comparable v, Comparable w){
  50. return v.compareTo(w) < 0;
  51. }//比较v是否小于w
  52. private static void exch(Comparable[] a, int i, int j){
  53. Comparable t = a[i];
  54. a[i] = a[j];
  55. a[j] = t;
  56. }//元素交换
  57. private static void show(Comparable[] a){
  58. for(int i = 0; i < a.length; i++)
  59. StdOut.print(a[i] + " ");
  60. StdOut.println();
  61. }//单行打印
  62. public static void main(String[] args){
  63. String[] a = In.readStrings();
  64. sort(a);
  65. show(a);
  66. }
  67. }

要点:

  1. 取样以子数组前三个数的中位值为切分元素: Comparable v = a[midNum(a,lo,hi)];
  2. midNum方法在返回中位元素索引同时,交换其与末尾元素,这样在partition内循环中,右子数组循环 while (less(a[i++],a[midNum(a,lo,hi)]));就无需边界检查,因为最终一定a[hi]==v退出循环;while (less(a[lo],a[j—]));j减为lo时一定a[lo]==v退出循环