1. 程式人生 > >快速排序(quickSort)

快速排序(quickSort)

str nlogn swap void spa 元素 完成 partition 一個

快速排序是最經典和常用的排序算法了,已經有不計其數的博客0 0

首先介紹下快速排序的原理。快速排序的基礎是基於這樣的事實:在一個序列中,如果一個節點前面的所有元素都不大於它,後面的所有元素都不小於它,那麽當整個序列達到有序狀態時,這個節點的位置保持不變。符合這樣條件的節點,稱為軸點(pivot)。

於是可以想到,如果能找到這個軸點,那麽該點就完成了排序,可以繼續在前、後的兩個子序列繼續尋找軸點。然而,軸點很可能是不存在的,因此,需要我們來構造軸點,即選定一個點,通過調整該點的前後子序列,使之符合軸點的要求。

軸點構造算法如下,總體思想就是,取出一個點作為軸點,然後從起始和末尾元素進行比較,發現不符合的元素,即把它調整到另一側。當兩者相匯時,即均已調整完成,算法即可結束。

 1 int partition(int* A, int lo, int hi)
 2 {
 3     swap(A[lo], A[lo + rand() % (hi - lo + 1)]);//隨機選取一個元素
 4     int pivot = A[lo];//作為軸點
 5     while (lo < hi)
 6     {
 7         while (lo < hi)
 8         {
 9             if (pivot < A[hi])
10                 hi--;
11             else
12             {
13 A[lo++] = A[hi]; break;//遇到相同元素直接轉到另一側 14 } 15 } 16 while (lo < hi) 17 { 18 if (pivot > A[lo]) 19 lo++; 20 else 21 { 22 A[hi--] = A[lo]; break;//同上 23 } 24 }
25 } 26 A[lo] = pivot;//位置已經調整好,把保存的軸點放回 27 return lo; 28 }

需要註意的一點是,如果判斷條件為<=時,如果有很多相同元素,會導致前後子序列劃分非常不均衡,遞歸深度為O(n),總體運行時間高達O(n^2)。因此盡可能讓前後序列長度相差較小,碰到相同元素即跳過並從另一邊繼續。

有了軸點構造,快速排序算法就非常容易了。每次軸點構造確定了mi的位置,只需要在前後子序列遞歸進行這一過程即可。

1 void quickSort(int* A, int lo, int hi)
2 {
3     if (hi - lo < 2) return;//遞歸基
4     int mi = partition(A, lo, hi - 1);
5     quickSort(A, lo, mi);//遞歸進行
6     quickSort(A, mi + 1, hi);
7 }

對於快速排序的復雜度,在最壞情況下可能會高達O(n^2),不過在平均效率上,為O(nlogn)。因此,快速排序並不一定真的非常快速0 0

另外,軸點構造算法在中位數和選取問題中也有應用,後面會寫一篇top-K問題,會詳細介紹軸點構造的應用。

快速排序(quickSort)