快速排序的思想非常简单,在待排序的数组中,先找到一个数字作为基准数,为了方便,一般选择数组的第一项作为基准数。接下来需要把这个待排序的数组中小于基准数的元素移动到待排序的数组的左边,把大于基准数的元素移动到待排序数组的右边。这时,左右两个分区的元素就相对有序了,接着把两个分区的元素的分别按照上面两个步骤继续对两个分区找到基准数,然后移动,直到各分区只有一个数时为止。

代码实现


  1. public class QuickSort {
  2. public static Integer[] sort(Integer[] array) {
  3. sort(array, 0, array.length - 1);
  4. return array;
  5. }
  6. /**
  7. * @param a
  8. * @param lo
  9. * @param hi
  10. */
  11. private static void sort(Integer[] a, int lo, int hi) {
  12. if (hi <= lo) return;
  13. int j = partition(a, lo, hi);
  14. sort(a, lo, j - 1);
  15. sort(a, j + 1, hi);
  16. }
  17. /**
  18. * 辅助函数:用于交换两个值
  19. *
  20. * @param array
  21. * @param a
  22. * @param b
  23. */
  24. public static void exchange(Integer[] array, int a, int b) {
  25. int tmp = array[a];
  26. array[a] = array[b];
  27. array[b] = tmp;
  28. }
  29. /**
  30. * 辅助函数:用于交换元素
  31. *
  32. * @param valueA
  33. * @param valueB
  34. * @return
  35. */
  36. public static Boolean less(Integer valueA, Integer valueB) {
  37. return valueA < valueB;
  38. }
  39. /**
  40. * 辅助函数:切分
  41. *
  42. * @param a
  43. * @param lo
  44. * @param hi
  45. * @return
  46. */
  47. private static int partition(Integer[] a, int lo, int hi) {
  48. int i = lo, j = hi + 1;
  49. Integer v = a[lo]; // 比较元素
  50. while (true) {
  51. // 扫描左右,检查扫描是否结束并交换元素
  52. while (less(a[++i], v)) if (i == hi) break;
  53. while (less(v, a[--j])) if (j == lo) break;
  54. if (i >= j) break; // 结束外层循环
  55. exchange(a, i, j);
  56. }
  57. exchange(a, lo, j); // 将 v = a[j] 放到正确的位置
  58. return j; // a[lo..j-1] <= a[j] <= a[j+1..hi]
  59. }
  60. public static void main(String[] args) {
  61. Integer[] array = new Integer[10];
  62. for (int i = 0; i < array.length; i++) {
  63. array[i] = (int) Math.floor((Math.random() * 10) + 1);
  64. }
  65. System.out.println(Arrays.toString(array)); // [9, 3, 2, 2, 6, 2, 5, 9, 1, 2]
  66. System.out.println(Arrays.toString(sort(array))); // [1, 2, 2, 2, 2, 3, 5, 6, 9, 9]
  67. }
  68. }

改进快排的两个方法

切换到插入排序

对于小数组,快速排序比插入排序慢。这是因为递归,快速排序的 sort() 方法在小数组中也会调用自己。

将 sort() 方法中的 if(hi <= lo) return; 改成 if(hi <= lo + M) Insertion.sort(a, lo, hi); return; 其中 M 取值范围是 5 ~ 15 之间。

三取样切分

这个方法适用于包含大量重复元素的数组。

  1. public class Quick3way {
  2. public static Comparable[] sort(Comparable[] array) {
  3. sort(array, 0, array.length - 1);
  4. return array;
  5. }
  6. /**
  7. * 辅助函数:用于交换两个值
  8. *
  9. * @param array
  10. * @param a
  11. * @param b
  12. */
  13. public static void exchange(Comparable[] array, int a, int b) {
  14. Comparable tmp = array[a];
  15. array[a] = array[b];
  16. array[b] = tmp;
  17. }
  18. private static void sort(Comparable[] a, int lo, int hi) {
  19. if (hi <= lo) return;
  20. int lt = lo, i = lo + 1, gt = hi;
  21. Comparable v = a[lo]; // v 始终都是不变的
  22. while (i <= gt) {
  23. int cmp = a[i].compareTo(v);
  24. if (cmp < 0) exchange(a, lt++, i++);
  25. else if (cmp > 0) exchange(a, i, gt--);
  26. else i++;
  27. } // 现在 a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi]成立
  28. System.out.println("***************************");
  29. System.out.println(Arrays.toString(a));
  30. System.out.println("***************************");
  31. sort(a, lo, lt - 1);
  32. sort(a, gt + 1, hi);
  33. }
  34. public static void main(String[] args) {
  35. Integer[] array = new Integer[10];
  36. for(int i = 0; i < array.length; i++) {
  37. Double v = Math.floor((Math.random() * 3) + 1);
  38. array[i] = v.intValue();
  39. }
  40. System.out.println(Arrays.toString(array)); // [1, 1, 1, 3, 2, 3, 3, 3, 3, 1]
  41. System.out.println(Arrays.toString(sort(array))); // [1, 1, 1, 1, 2, 3, 3, 3, 3, 3]
  42. }
  43. }

参考: