算法導論讀書筆記(7)
快速排序
快速排序是一種原地排序算法,對包含 n 個數的輸入數組,最壞狀況運行時間爲 Θ ( n2 )。雖然這個最壞狀況運行時間比較差,但快速排序一般是用於排序的最佳的實用選擇。這是由於其平均性能至關好:指望的運行時間爲 Θ ( n lg n ),且 Θ ( n lg n )記號中隱含的常數因子很小。java
像合併排序同樣,快速排序也是基於分治模式的。下面是對一個子數組 A [ p .. r ]排序的分治過程的三個步驟:算法
- 分解:
- 數組 A [ p .. r ]被劃分紅兩個(可能爲空)子數組 A [ p .. q - 1 ]和 A [ q + 1 .. r ],使得 A [ p .. q - 1 ]中的每個元素都小於等於 A [ q ],並且,小於等於 A [ q + 1 .. r ]中的元素。下標 q 也在這個劃分過程當中計算。
- 解決:
- 經過遞歸調用快速排序,對子數組 A [ p .. q - 1 ]和 A [ q + 1 .. r ]排序。
- 合併:
- 由於兩個子數組是就地排序的,將它們合併不須要操做,整個數組 A [ p .. r ]已排序。
下面的過程實現了快速排序:sql
QUICK-SORT(A, p, r) 1 if p < r 2 q = PARTITION(A, p, r) 3 QUICK-SORT(A, p, q - 1) 4 QUICK-SORT(A, q + 1, r)
快速排序算法的關鍵是 PARTITION
過程,它對子數組 A [ p .. r ]進行就地重排:數組
PARTITION(A, p, r) 1 x = A[r] 2 i = p - 1 3 for j = p to r - 1 4 if A[j] <= x 5 i = i + 1 6 exchange A[i] with A[j] 7 exchange A[i + 1] with A[r] 8 return i + 1
PARTITION
老是選擇一個 x = A [ r ]做爲 主元 (pivot element),並圍繞它來劃分子數組。在第3行到第6行中循環的每一輪迭代開始時,對於任何數組下標 k ,有bash
- 若是 p <= k <= i ,則 A [ k ] <= x
- 若是 i + 1 <= k <= j - 1,則 A [ k ] > x
- 若是 k = r ,則 A [ k ] = x
下圖總結了這一結構。過程 PARTITION
做用於子數組 A [ p .. r ]後獲得四個區域。 A [ p .. i ]中的各個值都小於等於 x , A [ i + 1 .. j - 1 ]中的值都大於 x , A [ r ] = x 。 A [ j .. r - 1 ]中的值能夠是任何值。dom
快速排序的簡單Java實現
private static int partition(int[] array, int p, int r) { int x = array[r]; int i = p - 1; for (int j = p; j < r; j++) if (array[j] < x) { i++; AlgorithmUtil.swap(array, i, j); } AlgorithmUtil.swap(array, i + 1, r); return i + 1; }
/** * 快速排序 */ public static void quickSort(int[] array) { quickSort(array, 0, array.length - 1); } private static void quickSort(int[] array, int p, int r) { if (p < r) { int q = partition(array, p, r); quickSort(array, p, q - 1); quickSort(array, q + 1, r); } }
快速排序的性能
快速排序的運行時間與劃分是否對稱有關,然後者又與選擇了哪個元素來進行劃分有關。若是劃分是對稱的,那麼本算法從漸近意義上來說,就和歸併排序算法同樣快;若是劃分是不對稱的,那麼本算法漸近上就和插入排序同樣慢。ide
最壞狀況劃分
快速排序的最壞狀況劃分發生在劃分過程產生的兩個區域分別包含 n - 1個元素和1個0元素的時候。假設算法的每一次遞歸調用都出現了這種不對稱劃分。劃分的時間代價爲 Θ ( n )。對一個大小爲0的數組進行遞歸調用後,返回 T ( 0 ) = Θ ( 1 ),故算法的運行時間爲 T ( n ) = T ( n - 1 ) + Θ ( n )。最終獲得解爲 T ( n ) = Θ ( n2 )。post
最佳狀況劃分
在 PARTITION
過程可能的最平衡劃分中,一個子問題的大小爲 FLOOR(n / 2)
,另外一個子問題的大小爲 CEIL(n / 2)
- 1。這種狀況下,其運行時間的遞歸式爲 T ( n ) <= 2 T ( n / 2 ) + Θ ( n )。該遞歸式的解爲 T ( n ) = O ( n lg n )。性能
快速排序的隨機化版本
隨機劃分使用 隨機取樣 (random sampling)的隨機化技術,從子數組 A [ p .. r ]中隨機選擇一個元素並與 A [ r ]互換,由於主元是隨機選擇的,咱們指望在平均狀況下,對輸入數組的劃分可以比較對稱。測試
RANDOMIZED-PARTITION(A, p, r) 1 i = RANDOM(p, r) 2 exchange A[r] with A[i] 3 return PARTITION(A, p, r)
比較排序
以前已經介紹了幾種能在 O ( n lg n )時間內排序 n 個數的算法。好比歸併排序,堆排序和快速排序。這些算法都有一個相同的性質:排序結果中,各元素的次序基於輸入元素間的比較。這類排序算法統稱爲 比較排序 。
在一個比較排序算法中,僅用比較來肯定輸入序列< a1 , a2 , …, an >的元素間次序。就是說,給定兩個元素 ai 和 aj ,測試 ai < aj , ai <= aj , ai = aj , ai >= aj 或 ai > aj 中的哪個成立,以肯定 ai 和 aj 之間的相對次序。
比較排序能夠被抽象地視爲 決策樹 。一棵決策樹是一棵滿二叉樹,表示某排序算法做用於給定輸入所作的全部比較,而控制結構,數據移動等都被忽略了。下圖是插入排序做用於含三個元素的輸入序列上的決策樹。
在決策樹中,對每一個內結點都註明 i : j ,其中1 <= i , j <= n , n 是輸入序列中的元素個數。對每一個葉結點都註明排列< π ( 1 ), π ( 2 ), …, π ( n )>。排序算法的執行對應於遍歷一條從樹根到葉結點的路徑。在每一個內結點處都要作比較。當到達一個葉結點是,排序算法就肯定了順序。要使排序算法能正確地工做,其必要條件是, n 個元素的 n! 中排列中的每一種都要做爲決策樹的一個葉子出現。