歸併排序 (MergeSort)
歸併排序:偉大的統一者
演算法背後的故事:第一份草案的天才
1945 年,20 世紀最偉大的頭腦之一——約翰·馮·諾伊曼 (John von Neumann) 正在撰寫著名的《關於 EDVAC 報告的第一份草案》。在書中,他描述了一種不涉及快速排序那種「混亂交換」的排序方法。
他設想了一個世界:妳先完美地解決小問題,然後將她們合併。如果妳手頭有兩疊已經排好序的卡片,將她們合併成一疊巨大的排好序的卡片是非常容易的:妳只需要看每疊最上面的那張,然後挑出較小的那張即可。
馮·諾伊曼的 歸併排序 (MergeSort) 成為了 穩定性 (Stability) 的黃金標準。它是第一個證明我們可以保證在 時間內排序任何規模的數據,且絕不會破壞相等元素原始內部順序的演算法。
為什麼需要它?
歸併排序是演算法界的「外交官」。
- 穩定性: 如果妳先按「姓名」排序客戶列表,再按「城市」排序,歸併排序能確保在每個城市內部,姓名依然是按字母順序排列的。快速排序則無法保證這一點。
- 外部排序: 當數據量大到記憶體裝不下(例如 10TB 日誌)時,歸併排序是唯一選擇。妳在磁碟上對小塊數據排序,然後將她們「歸併」在一起。
- 鏈表排序: 因為她不需要「隨機存取」(她只看「下一個」元素),她是對鏈表進行排序最有效的方法。
演算法是如何「思考」的
這個演算法像是一個耐心的建築師,自底向上地建構秩序。
原子化 (分解): 它拿出一份混亂的列表,不斷將她切半,直到每個元素都孤身一人。按照定義,單個元素本身就是「排好序的」。
外交統一 (合併): 這是核心魔術所在。它將兩個排好序的列表合併。它扮演著守門人的角色:
「我有兩組排好序的隊伍。誰手裡拿的數字最小?是妳?請出列。下一個又是誰?」
遞迴增長: 透過合併兩個「1 元素列表」,它創造了一個「2 元素排序列表」。透過合併兩個「2 元素列表」,它創造了一個「4 元素列表」。它不斷進行,直到整個世界都在一面有序的旗幟下完成統一。
工程決策:記憶體稅
歸併排序非常可靠,但她是有代價的:空間。
- 權衡: 與快速排序不同,歸併排序通常需要一個與被排序數據大小相等的「臨時倉庫」(額外記憶體),即 空間。
- 性能保證: 她的時間複雜度永遠是 。她沒有像快速排序那樣的「最壞情況」噩夢。她是演算法世界中的中流砥柱。
實作參考 (Python)
def mergesort(arr):
# 基礎情況:原子已經是排好序的
if len(arr) <= 1:
return arr
# 1. 將世界切分為兩半
mid = len(arr) // 2
left_half = mergesort(arr[:mid])
right_half = mergesort(arr[mid:])
# 2. 外交統一 (合併)
return merge(left_half, right_half)
def merge(left, right):
result = []
i = j = 0
# 比較並從兩邊挑選最小的
while i < len(left) and j < len(right):
if left[i] <= right[j]: # '<=' 確保了穩定性!
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
# 處理剩餘的倖存者
result.extend(left[i:])
result.extend(right[j:])
return result小結
歸併排序教會我們:統一建立在微小的確定性之上。透過專注於合併那些已經是正確的東西,我們可以創造一個穩定且有序的整體。它提醒我們,在工程中,「快」固然好,但「穩定且可預測」往往更具價值。
