Luke a Pro

Luke Sun

Developer & Marketer

🇺🇦
EN||

歸併排序 (MergeSort)

| , 2 minutes reading.

歸併排序:偉大的統一者

演算法背後的故事:第一份草案的天才

1945 年,20 世紀最偉大的頭腦之一——約翰·馮·諾伊曼 (John von Neumann) 正在撰寫著名的《關於 EDVAC 報告的第一份草案》。在書中,他描述了一種不涉及快速排序那種「混亂交換」的排序方法。

他設想了一個世界:妳先完美地解決小問題,然後將她們合併。如果妳手頭有兩疊已經排好序的卡片,將她們合併成一疊巨大的排好序的卡片是非常容易的:妳只需要看每疊最上面的那張,然後挑出較小的那張即可。

馮·諾伊曼的 歸併排序 (MergeSort) 成為了 穩定性 (Stability) 的黃金標準。它是第一個證明我們可以保證在 O(NlogN)O(N \log N) 時間內排序任何規模的數據,且絕不會破壞相等元素原始內部順序的演算法。

為什麼需要它?

歸併排序是演算法界的「外交官」。

  • 穩定性: 如果妳先按「姓名」排序客戶列表,再按「城市」排序,歸併排序能確保在每個城市內部,姓名依然是按字母順序排列的。快速排序則無法保證這一點。
  • 外部排序: 當數據量大到記憶體裝不下(例如 10TB 日誌)時,歸併排序是唯一選擇。妳在磁碟上對小塊數據排序,然後將她們「歸併」在一起。
  • 鏈表排序: 因為她不需要「隨機存取」(她只看「下一個」元素),她是對鏈表進行排序最有效的方法。

演算法是如何「思考」的

這個演算法像是一個耐心的建築師,自底向上地建構秩序。

  1. 原子化 (分解): 它拿出一份混亂的列表,不斷將她切半,直到每個元素都孤身一人。按照定義,單個元素本身就是「排好序的」。

  2. 外交統一 (合併): 這是核心魔術所在。它將兩個排好序的列表合併。它扮演著守門人的角色:

    「我有兩組排好序的隊伍。誰手裡拿的數字最小?是妳?請出列。下一個又是誰?」

  3. 遞迴增長: 透過合併兩個「1 元素列表」,它創造了一個「2 元素排序列表」。透過合併兩個「2 元素列表」,它創造了一個「4 元素列表」。它不斷進行,直到整個世界都在一面有序的旗幟下完成統一。

工程決策:記憶體稅

歸併排序非常可靠,但她是有代價的:空間

  • 權衡: 與快速排序不同,歸併排序通常需要一個與被排序數據大小相等的「臨時倉庫」(額外記憶體),即 O(N)O(N) 空間。
  • 性能保證: 她的時間複雜度永遠O(NlogN)O(N \log N)。她沒有像快速排序那樣的「最壞情況」噩夢。她是演算法世界中的中流砥柱。

實作參考 (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

小結

歸併排序教會我們:統一建立在微小的確定性之上。透過專注於合併那些已經是正確的東西,我們可以創造一個穩定且有序的整體。它提醒我們,在工程中,「快」固然好,但「穩定且可預測」往往更具價值。