1. 程式人生 > 其它 >演算法|快速排序

演算法|快速排序

完成閱讀您將會了解快速排序的:

  1. 演算法思想
  2. 實現步驟
  3. 實踐範例(C++/Rust)

1. 演算法思想

快速排序Quick Sort),簡稱快排,最早由 C.A.R.Hoare 在1962年於快速排序[1]一文提出。快速排序實質上運用分治Divide & Conquer)思想,每次選取基準元素Pivot Element),並將剩餘元素在其左右分為小於不小於基準元素的兩個子序列Sub-sequence),然後針對子序列遞迴地進行快速排序,如圖1[2]

圖1:快速排序

快速排序為不穩定排序Unstable Sort),即同值元素排序後未必保持原有相對位置,如圖2-3[3]。快速排序時間複雜度為\(O(n^2)\)

,但實際運用中,攤銷Amortized)時間複雜度為\(O(n\lg n)\)。快速排序是一種原址排序In-place Sort)演算法,額外的空間複雜度為\(\Theta (1)\)。快速排序演算法適合硬體快取記憶體Cache)優化,擁有很好的實際執行效率,並被廣泛使用。

圖2:穩定排序
圖3:不穩定排序

2. 實現步驟

  1. 選擇基準元素;(序列尾,本文程式範例中隨機選取基準)
  2. 將不小於和不大於基準元素的元素分割槽為左右兩個子序列,與此同時基準元素本身已完成排序,如圖4[3:1]
  3. 遞迴排序子序列。
圖4:序列分割槽

3. 實踐範例(C++/Rust)

問題描述
為無序陣列A做單調不減排序。
輸入

:無序陣列A
輸出:有序陣列A
解答思路
運用快速排序演算法對A進行原址排序。


虛擬碼1:分割槽
變數說明:A\(\rightarrow\)待排序陣列;l\(\rightarrow\)子序列左閉邊界;r\(\rightarrow\)子序列右閉邊界;p\(\rightarrow\)分割槽點;iter\(\rightarrow\)迭代替量

\[\begin{aligned} &PARTITION(A,l,r) \\ &~~~~~~ p \leftarrow l \\ &~~~~~~iter \leftarrow p \\ &~~~~~~while ~~~iter<r \\ &~~~~~~~~~~~~if~~~A[iter]<A[r] \\ &~~~~~~~~~~~~~~~~~~swap(A[iter],A[p]) \\ &~~~~~~~~~~~~~~~~~~p \leftarrow p+1 \\ &~~~~~~~~~~~~iter \leftarrow iter+1 \\ &~~~~~~swap(A[p],A[r]) \\ &~~~~~~return ~~~p \end{aligned} \]

虛擬碼2

:快速排序A
變數說明:A\(\rightarrow\)待排序陣列;l\(\rightarrow\)子序列左閉邊界;r\(\rightarrow\)子序列右閉邊界;p\(\rightarrow\)分割槽點

\[\begin{aligned} &QUICKSORT(A,l,r) \\ &~~~~~~ if~~~l<r \\ &~~~~~~~~~~~~p \leftarrow PARTITION(A,l,r) \\ &~~~~~~~~~~~~QUICKSORT(A,l,p-1) \\ &~~~~~~~~~~~~QUICKSORT(A,p+1,r) \\ \end{aligned} \]

C++解答:傳指標入參

constexpr auto _partition(auto l, auto r) noexcept {
  auto p = l, iter = p - 1;
  auto pivot = *r;
  while (++iter < r)
    if (*iter < pivot)
      std::swap(*iter, *p++);
  std::swap(*p, *r);
  return p;
}

void quick_sort(auto l, auto r) noexcept {
  srand(time(nullptr));
  if (l < r) {
    std::swap(*(l + rand() % (r - l + 1)), *r);
    auto p = _partition(l, r);
    quick_sort(l, p - 1);
    quick_sort(p + 1, r);
  }
}

Rust解答

fn _partition<T: Clone + std::cmp::PartialOrd>(A: &mut Vec<T>, l: usize, r: usize) -> usize {
    let (mut p, pivot) = (l, A[r].clone());
    for i in l..r {
        if A[i] < pivot {
            A.swap(i, p);
            p += 1;
        }
    }
    A.swap(p, r);
    p
}

pub fn quick_sort<T: Clone + std::cmp::PartialOrd>(A: &mut Vec<T>, l: usize, r: usize) {
    if l < r {
        A.swap(thread_rng().gen_range(l..=r), r);
        let mut p = _partition(A, l, r);
        quick_sort(A, l, p - 1);
        quick_sort(A, p + 1, r);
    }
}

4. 自我測試

虛擬碼實踐
快排遞迴版受棧記憶體限制,無法直接應用在大規模資料上,請將快排改寫成迭代版。

LeetCode選薦

  1. Sort an Array

測試參考解答

讓每一天足夠精緻,期待與您的再次相遇! ^_^


  1. C, A, R, et al. Quicksort[J]. The Computer Journal, 1962, 5(1):10-16. ↩︎

  2. 圖片引自Wikipedia,在此鳴謝。 ↩︎

  3. 圖片引自tutorialspoint,在此鳴謝。 ↩︎ ↩︎