1. 程式人生 > >千磨萬擊還堅韌,任爾東西南北風

千磨萬擊還堅韌,任爾東西南北風

快速排序

快速排序的基本思想是通過劃分子陣列實現的,對於陣列A[p…r],劃分子陣列後得到一個索引q,使得A[p…q-1]子陣列的值都小於A[q],A[q+1…r]子陣列的值都大於等於A[q]。子陣列劃分後,再通過相同的方法遞迴處理兩個子陣列,最終達到整個陣列排序的目的。

快速排序虛擬碼

先看下虛擬碼:

quick_sort(A, p, r)
  if (p < r)
    int q = partition(A, p, r);
    quick_sort(A, p, q - 1);
    quick_sort(A, q + 1, r);

虛擬碼的思路很簡單,快速排序最重要的是劃分子陣列,即partition的實現,先看下partition示意圖

這裡寫圖片描述


劃分子陣列過程中將陣列分為四個區域,首先將A[r]=x選為主元,劃分子陣列結束後A[q]的值就是x,即將陣列劃分為小於x和不小於x的兩個部分。

  • 區域1:這個區域的值小於x
  • 區域2:這個區域的值大於等於x
  • 區域3:這個區域的值和x大小是未知的,是將要消除的區域
  • 區域4:主元

劃分子陣列虛擬碼

劃分子陣列的過程即是消除區域3的過程,最終指標j等於r,看下partion的虛擬碼:

partition(A, p, r)
  x = A[r]
  i = p - 1
  for j = p to r - 1
    if A[j] < x
        i = i + 1
exchange(A[i], A[j]) exchange(A[i+1], A[r]) return i + 1

根據quick_sort和partion的虛擬碼,很容易寫出java程式碼:

public class QuickSort {

    /**
     * 快速排序
     * @param arr 待排序的陣列
     * @param left 起始索引
     * @param right 結束索引
     */
    public static void quickSort(int[] arr, int left, int right) {
        if
(left < right) { int q = partition(arr, left, right); quickSort(arr, left, q - 1); quickSort(arr, q + 1, right); } } /** * 劃分子陣列,返回值為q,則arr[left...q-1]子陣列的值都小於arr[q], * arr[q+1...right]子陣列的值都不小於arr[q] * @param arr * @param left * @param right * @return */ private static int partition(int[] arr, int left, int right) { int i = left - 1; int x = arr[right]; for (int j = left; j < right; j++) { if (arr[j] < x) { i++; exchange(arr, i, j); } } exchange(arr, i + 1, right); return i + 1; } private static void exchange(int[] arr, int i, int j) { int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } }

複雜度分析

空間複雜度O(1)

快速排序的時間效能取決於劃分的效能,如果每次劃分都將子陣列劃分為差不多相等的兩部分,那麼排序效能會非常好,此時時間複雜度為O(nlgn)

如果每次劃分都將陣列劃分為大小為n-1和0的子陣列時,此時排序效能會非常差,時間複雜度為O(n2)

最壞時間複雜度O(n2)

最好時間複雜度O(nlgn)

平均時間複雜度O(nlgn)