ROSALIND|Quick Sort (QS)
使用 quick sort 排序長度為 n 的整數數列
Given: A positive integer $n$ $\leq 10^5$ and an array $A[1..n]$ of integers from $-10^5$ to $10^5$.
Return: A sorted array $A[1..n]$.
(https://rosalind.info/problems/qs/)
Quick sort 是基於劃分 (partition) 的排序方法。考慮一個長度為 2 的數列,若隨機挑選一個數字作為基準值,將數列劃分為小於或大於基準值的兩部分。由於各部分僅含一個數字,所以劃分的同時也確定了順序。
不過,如果數列長度大於 2 就只能保證劃分區域間的關係,無法確認個別區域內的數字順序。舉例來說,若以數字 3
為基準 [1, 2, 3, 4, 5]
和 [2, 1, 3, 5, 4]
都滿足劃分的定義,但無法保證基準兩側的數字順序。我們得進一步劃分兩側的子數列,直到各部分的劃分反映其排序為止,這便是 Quick sort 的核心策略。
為此,我們需要一個的劃分數列的輔助函數。由於要在原數列進行排序,而且每次只劃分基準值兩側順序未定的區域,所以要在程式碼裡添加參數(L
:左側,R
:右側)來界定劃分範圍。
1 | def partition(arr: list[int], pivot_index: int, L: int, R: int) -> tuple[int, int]: |
Quick sort 的本體則是透過遞迴關係執行劃分的函數,它持續劃分基準點兩側順序未定的區域,直到劃分出的區域內只剩一個數字,即劃分方式能完整反映數列順序為止。
1 | import random |
Quick sort 的執行效率取決於基準值的選取。假設數列的長度為 n,並以其中最大或最小值為基準,那麼每次只會劃出一個數字,例如 [5, 4, 3, 2, 1]
會分為 [5]
和 [4, 3, 2, 1]
。這樣的劃分方式需要依序處理長度為 n-1、n-2、…、1 的子數列。由於每次劃分的時間複雜度為 $O(n)$,而剩餘數列的長度為 n-1、n-2、…、1 的等差級數,所以最差情境的時間複雜度為 $O(n^2)。
反之,如果每次選取的基準點恰好將數列分為兩半,那麼遞迴層數變為 $logn$,時間複雜度也降為 $O(nlogn)$。因此,實作時為了避免最差情境,會隨機選取數字作為劃分的基準點。