選擇問題只要求一個指定排名的元素,排序卻要給出全部元素的完整次序。這個 區別使 quickselect 可以捨棄不會影響目標排名的工作。Counting sort 與 LSD radix sort 更進一步:它們利用 key 的額外結構跨過比較排序下界,但必須清楚 說明值域、位數與計算模型。
動機
假設一個 list 有數百萬個值,而我們只需要中位數。先完整排序當然能夠得到 答案,可是完整次序所含的資訊遠多於一個中位數。選擇演算法之前,先確認題目 實際要求甚麼 output,是重要的演算法設計習慣。
倉庫內的 tutorial 依次給出三層思路。完整排序是容易理解的基線;partial selection sort 只放好最前面的若干 order statistics;quickselect 重用 quicksort 的 partition,但只進入可能包含目標的一邊。其後,linear-time sorting 講義改變計算模型:有界整數可以直接計數,多位 key 則可以逐位穩定 排序。
本節處理的是靜態、單次查詢。若資料不更新而要查詢很多個排名,先排序一次 可能合理;若查詢之間還會頻繁插入和刪除,dynamic selection tutorial 指向 維護 subtree size 的高度平衡搜尋樹。它的對數保證依賴平衡性和 metadata 的 正確維護,所以完整實作應放在 tree notes;這裏只交代靜態選擇與 dynamic order statistics 的邊界。
定義
定義
選擇問題
給定含 個 key 的 list ,以及滿足 的整數 ,返回 第 小的 key。排名採用 1-based,偽代碼中的 array index 可以採用 0-based。重複值按出現次數分別計入:在 中,第 1 小和第 2 小都是 。
與 分別是 minimum 與 maximum,只需一次掃描,所以 comparison model 下的 selection 並不存在一般性的 下界。後文的下界 針對輸出完整次序的排序,而不是只找一個排名。
Partial selection sort 執行 selection sort 的前 輪。第 輪找出剩餘
suffix 的最小值,與位置 交換,最後返回 A[k - 1];array 實作無需真的
刪除元素。比較次數恰好是
所以是 。固定常數 時為線性;當 時則為二次。
定義
Partition 契約
對長度為 的目前 subarray,Partition 返回 index ,把 pivot 放在
A[p],並保證 左邊每個 key 都不大於 A[p],右邊每個 key 都不小於
A[p]。因此即使有重複值,pivot value 也是局部第 小的一個合法值。
這個「pivot 最終 index」契約比只返回兩個區域邊界的 partition 更強。
若 是相對於目前 subarray 的 1-based rank,正確分支結構是:
Quickselect(A, m, k):
require 1 ≤ k ≤ m
if m == 1: return A[0]
p = Partition(A, m) // p 是 pivot 最終的 0-based index
if k - 1 == p: return A[p]
if k - 1 is below p: return Quickselect(A[0:p], p, k)
return Quickselect(A[p+1:m], m-p-1, k-p-1)
相等分支不可省略。進入右邊時,新 rank 要減去左邊的 個位置和 pivot 本身。
每次 recursive call 開始時都保持這個 invariant:目前 subarray 含有原題所求的 order statistic,而參數 是它在目前 subarray 內的 rank。左邊 call 沒有 丟棄任何更小位置,所以保留 ;右邊 call 恰好丟棄 個位置,所以使用 。Equality return 在 pivot 處停止,而且兩個 recursive branches 都 嚴格短於 parent,因此 base case 必會到達。若 partition 返回的是 Hoare-style boundary 而不是 pivot 最終 index,就需要另一套論證,不能原樣代入這段 pseudocode。
Randomized three-way quickselect
上面的 source teaching trace 使用 two-way final-index partition。若 input 可能有 duplicates,而我們要證明 expected-time guarantee,應另用 three-way variant:從目前 records 中 uniform random 選一個 pivot,再分成 strict-less block 、equal block 與 strict-greater block 。令 是 1-based local rank,、:
RandomizedQuickselect(A, m, k):
pivot = key of a uniformly random record in A
(L, E, G) = ThreeWayPartition(A, pivot)
if k ≤ |L|: return RandomizedQuickselect(L, |L|, k)
if k ≤ |L| + |E|: return pivot
return RandomizedQuickselect(G, |G|, k-|L|-|E|)
這裏保持同一個 recursive-call invariant,但 greater branch 丟棄的 prefix 大小 變成 。每次 recursive call 都只進入 strict block,所以規模嚴格 縮小;若所有 keys 相等, 就是整個 input,algorithm 做一次 partition 後直接 return。這個 three-way contract 也不同於 Hoare boundary,必須按自身語義實作和證明。
定義
穩定排序
若具有相同 key 的 records 在 output 中保持 input 的相對次序,sorting routine 就是 stable。當後續 pass 按 compound key 的另一部分排序,而又 必須保留較早 pass 建立的次序時,stability 是正確性條件。
定義
Counting sort
若整數 key 落在 inclusive range ,標準 stable counting sort 需要 個 counters。它先統計頻率,再求 prefix counts,最後從右到左掃描 input,把 records 放入獨立 output array。時間為 ;若同時計算 output 與 count arrays,auxiliary space 為 。這個標準 stable 版本不是 in-place。
定義
LSD radix sort
設每個非負 key 在 base 下有 位。Least-significant-digit (LSD) radix sort 先對 digit 做 stable sort,再處理 digit ,直至 digit 。若每一輪用 counting sort 處理 種 digit value,總時間為 ,auxiliary space 為 。
對於 -bit machine words,若每個 digit 取 bits,則 、,所以成本為 。較大的 digit 減少 passes,卻擴大 counter array;較小的 digit 使用更緊湊的 counters,卻要多次掃描 array。
所以「linear」是有條件的: 時 counting sort 才是 ;若 representation 使 為常數且 ,LSD radix sort 才是 。負數、變長表示或巨大 key universe 都需要額外處理,不能直接 宣稱無條件線性。
定理 / 命題
定理
Quickselect 為何不需要完整排序
假設 Partition 滿足 pivot-final-index 契約。若返回 index ,局部第
小就是 pivot;更小的目標 rank 在 left subarray;更大的目標 rank 在
right subarray,並調整為 。因此每次 partition 後至多只需一次
recursive call。
定理
比較排序的 decision-tree 下界
對於由 個互異 key 組成的任意 input,任何只透過 key comparison 得知 相對次序的 deterministic sorting algorithm,都存在需要 次 comparisons 的 worst-case execution。若有界整數可以 用作 array index,這個模型假設便不再適用。
定理
LSD radix sort 的正確性
完成 digit 的 stable passes 後,array 按照由這 個 低位組成的 base- suffix 排好。處理全部 位後,這個 suffix 就是完整 key,所以 array 完全有序。
證明思路
Quickselect 的分支正確性與成本
Partition 後,pivot 位於它在完整 sorted current subarray 中可以佔據的最終 位置;左邊所有 key 不大於它,右邊所有 key 不小於它。因此 target index 小於 時毋須右邊,大於 時毋須左邊,相等時立即 return。兩邊內部毋須已經 有序。
每次 partition 在長度 的 subproblem 上花 。若 pivot 每次都 極端,size 依次為 ,所以 。若每次恰好縮半, 的 geometric sum 只是線性成本的直覺,不能單獨當作 average-case proof。
對 randomized three-way variant,明確假設每次從目前 subarray 中 uniform、independent 地選擇一個 record,並且 comparison 與 swap 是 constant time。把相等 occurrences 任意賦予 sorted order 中連續的 ranks。 所選 record 的 rank 落在中間一半的概率至少為 ;一旦發生,它的 strict-less 與 strict-greater blocks 都至多為 。目標要麼在 equal block 直接 return,要麼只在其中一個 strict block 繼續。得到一次這種 constant-factor shrink 所需嘗試次數期望至多為 2,每個 size scale 的 expected work 是 。對逐層縮小的 scales 求和得到 expected ; 第一次 partition 又給出 ,所以 expected time 為 。 All-equal input 只需一次 linear pass;若 keys 互異而 pivots 每次都極端, worst case 仍為 。
Decision-tree 下界證明
個互異 arbitrary keys 有 種相對次序。Deterministic comparison sort 可以表示成 binary decision tree:internal node 是一次 comparison,leaf 必須識別一種 input permutation,所以正確演算法至少需要 個 leaves。 高度 的 binary tree 最多有 個 leaves,因此 。令 。 中最大的 個 factors 都至少為 ,所以
所以至少一個 input 要走過這麼長的 path。Counting sort 與 radix sort 使用 digit extraction、arithmetic 和 direct addressing,並限制 key representation,因此不違反這個只針對 comparison model 的結論。
LSD radix invariant
對 pass 數作 induction。Digit 排完後,array 按最低位有序。假設已經按 digits 至 組成的 suffix 有序;下一次 stable pass 按 digit 排序。Current digit 不同的 records 由本輪直接排好;current digit 相同的 records 保持上一輪相對次序,因此仍按較低位 suffix 排好。於是處理 digit 後,array 按更長 suffix 有序。直至 digit ,suffix 等於完整 key。
例題詳解
例題
Partial selection 的成本
對 tutorial array
找第 4 小,partial selection 依次把
放到前四個位置。 時,四次 suffix scan 共作
次 comparisons,返回 A[3]=7。
完整 selection sort 要作 次 comparisons;這裏節省工作是因為 小。 若找 median,,partial selection 仍為 quadratic。
例題
Quickselect partition trace
在 中找第 2 小,並採用 tutorial 的 first-element pivot。
- 以 partition,得到 。Pivot index 為 、rank 為 ,所以只保留 left subarray 。
- 以 partition,pivot 局部 rank 為 。目標在右邊,新 rank 是 。
- 在 中,pivot 的局部 rank 為 ;相等分支 return 。
被捨棄部分毋須 sort。這個 trace 亦說明 equality return 與 right-rank adjustment 缺一不可。
例題
Counting sort:從計數到 stable placement
令 且 。Keys 至 的六個 counters 為
Prefix accumulation 後得到
其中 是不大於 的 key 數。從右到左掃描 ,對 key 執行
B[--C'[x]] = record。例如最右的 先把 counter 減為 ,再放到
zero-based index 。最終
若相同 key 帶有不同 record identity,從右到左處理會讓 input 中較後的 record 佔據較後的 output position,從而保持相對次序。三階段成本分別是 、、,合計 。
例題
Radix sort 為何需要 stability
對 執行 base- LSD radix sort:
- stable ones-digit pass:;
- stable tens-digit pass:;
- stable hundreds-digit pass:。
以 tens pass 為例,tens digit 同為 的 與 保持上一輪根據 ones digit 建立的次序。Unstable pass 可能把它們逆轉,破壞 induction hypothesis。
常見錯誤
- 接受 或 ,或混淆 1-based rank 與 0-based index。
- 把普通 partition boundary 當成 pivot 最終 index。
- 省略 quickselect 的 equality return,或進入右邊時沒有減去 。
- 說 quickselect 永遠 linear,沒有 uniform randomized-pivot model 就把 balanced shrink 當成 average-case proof,或在允許 duplicates 時漏掉 three-way equal block。
- 寫 sorting「至少是 」。下界要寫 ,並要說明 arbitrary distinct inputs 與 comparison-only assumptions。
- 對 inclusive range 只分配 個 counters,或沒有把全部 個 counters 初始化為零。
- 把標準 stable counting sort 說成 in-place,或在遞減 prefix position 時 從左到右掃描 input。
- 對所有 radix-sort organization 作同一個 stability 聲明;本節定理專指 whole-array LSD passes。
總結
- Selection 只返回一個 order statistic;sorting 返回全部 rank information。
- Partial selection 成本為 ,只在 小時有吸引力。
- Source two-way final-index trace 讓 quickselect 只進入一邊;duplicate-safe uniform randomized three-way variant 會在 equal block 直接 return,expected time 為 。All-equal input 是 ,worst case 是 。
- Arbitrary distinct keys 的 comparison sorting 由 decision-tree proof 得到 worst-case comparisons。
- 對 keys ,stable counting sort 使用 個 counters,時間 ,從右到左掃描以保證 stability;標準 output-array 版本不是 in-place。
- 含 個 base- digits 的 LSD radix sort 使用 stable digit passes,成本 ;是否線性取決於 representation parameters。
練習
思考檢查
為甚麼 full sorting 比 selection 做更多工作?
比較題目要求的 output。
解答 · 答案
Selection 只要一個 ranked element;sorting 要知道所有元素的完整次序。
思考檢查
Counting sort 線性時間需要甚麼額外假設?
想 count array 的大小。
解答 · 答案
Key range 必須受控;常見寫法是 K = O(n)。
- 解釋 quickselect 為甚麼只 recurse 一邊,並包括 equality case。
- 各舉一個 partial selection sort 合理與不合理的情況。
- 解釋 LSD radix sort 為甚麼需要 stable digit subroutine。
- 當 時,求 suffix-scan partial selection 的準確 comparison count。
- 從 prefix counts 出發,說明最右的 key 放到哪裏, 其 counter 變成多少。
- 嚴格解釋 counting sort 與 radix sort 為甚麼沒有違反 comparison-sorting lower bound。
答案與解答
解答 · 引導解答
- Pivot 最終 index 已知後,目標要麼等於 pivot,要麼嚴格在左邊或右邊。 相等便 return;其餘情況只有相關一邊可能包含目標。
- 當 是小常數,只需少量 minimum-selection rounds 時合理;找 median 等 的情況會變成 quadratic。
- Stability 保存 lower-digit passes 在 current-digit ties 中建立的次序; 否則後續 pass 會破壞較早的 digit 資訊。
- 次 comparisons。
- Counter 從 減為 ,record 放在 zero-based output index 。
- 下界假設 arbitrary distinct keys 且只透過 comparisons 學習次序。 Counting 與 radix sort 限制 key representation,並使用 arithmetic、 digit extraction 和 direct addressing。