↓快轉到主要內容
  1. LeetCode/

LeetCode 912: Sort an Array

·2 分鐘· ·
LeetCode Medium Sorting Merge-Sort Divide-and-Conquer
Wei Yi Chung
作者
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
目錄

基本資料
#

難易度: medium 第一次嘗試:2026-10-18 來源:Merge sort 練習梯子,第 0 階

解題思路
#

不能用內建排序,要在 O(n log n) 內排好。這題不建議用 quick sort:測資有大量重複值和已排序的陣列,pivot 選不好會退化成 O(n^2)。Merge sort 最壞也是 O(n log n)。

我的嘗試
#

第一版:右半邊沒有被排序
#

mid = (lo + hi) // 2
sort(lo, mid)
sort(mid, lo)        # 應該是 sort(mid, hi)

sort(mid, lo) 進去時 hi - lo 算出來是 lo - mid,一定 <= 0,直接 return。用 [5, 2, 3, 1] 跑會得到 [2, 3, 1, 5]。

當時我不懂為什麼「最外層還是有 merge 0:n」卻會錯。原因是 merge 只會合併,不會排序:

  • merge 每一步只比較兩邊的隊頭,前提是隊頭就是那半邊的最小值。
  • merge 不會改變同一半內部的順序。右半原本 3 在 1 前面,合併後 3 還是在 1 前面。

所以兩半必須先被遞迴排好。另一個角度:如果一次 O(n) 的 merge 就能排好任何陣列,比較排序就能做到 O(n),但比較排序的下限是 O(n log n)。

第二版:merge 時讀錯陣列
#

temp[k] = temp[i]    # 應該是 nums[i]
temp[k] = temp[j]    # 應該是 nums[j]

temp 是用來寫入的,從它讀到的是 0 或上一次 merge 留下的舊資料。[5, 2, 3, 1] 會變成 [0, 0, 0, 0]。

規則:merge 時 nums 只讀、temp 只寫,賦值號左右兩邊不應該出現同一個陣列名稱。

正確解法
#

class Solution:
    def sortArray(self, nums: list[int]) -> list[int]:
        n = len(nums)
        temp = [0] * n

        def sort(low: int, high: int):
            if high - low <= 1:
                return

            mid = (low + high) // 2
            sort(low, mid)
            sort(mid, high)

            i, j, k = low, mid, low
            while i < mid or j < high:
                if j >= high or (i < mid and nums[i] <= nums[j]):
                    temp[k] = nums[i]
                    i += 1
                else:
                    temp[k] = nums[j]
                    j += 1
                k += 1

            nums[low:high] = temp[low:high]

        sort(0, n)
        return nums

複雜度
#

  • 時間 O(n log n):切 log n 層,每層 merge 的總工作量是 O(n)。遞迴式是 T(n) = 2T(n/2) + O(n)。不管輸入長怎樣都會切滿,所以最好情況也是 O(n log n)。
  • 空間 O(n):temp 加上 O(log n) 的遞迴深度。

要特別避免的錯誤
#

  • 區間寫法混用:左閉右開就統一用 [low, high),終止條件 high - low <= 1。
  • merge 完忘記抄回 nums。
  • 每次 merge 都新開 list:還是 O(n log n),但常數大,Python 有機會 TLE。
  • 用 < 而不是 <=:這題不影響結果,但排序會不穩定,之後的計數題會算錯。

面試口說整理
#

我用 merge sort,因為它最壞也是 O(n log n),不像 quick sort 會被重複值或已排序的輸入打到 O(n^2)。遞迴把兩半各自排好,再用雙指針合併到暫存陣列,最後抄回原陣列。總共 log n 層,每層 O(n),所以時間 O(n log n),空間是 O(n) 的暫存陣列。用 <= 讓排序保持穩定。

相關文章

LeetCode 148: Sort List
·1 分鐘
LeetCode Medium Linked-List Two-Pointers Divide-and-Conquer Sorting
LeetCode 148 解題筆記:用快慢指針切成兩半,遞迴排序後合併;O(n log n),不需要額外陣列
LeetCode 324: Wiggle Sort II
·1 分鐘
LeetCode Medium Array Divide-and-Conquer Greedy Sorting
LeetCode 324 解題筆記:排序後分成小的一半和大的一半,各自反轉後交錯放:小的放偶數位置、大的放奇數位置
LeetCode 215: Kth Largest Element in an Array
·1 分鐘
LeetCode Medium Array Divide-and-Conquer Sorting Heap-Priority-Queue
LeetCode 215 解題筆記:維持大小為 k 的 min-heap,堆頂就是第 k 大;或用 quickselect 平均 O(n)
LeetCode 347: Top K Frequent Elements
·1 分鐘
LeetCode Medium Array Hash-Table Divide-and-Conquer Sorting
LeetCode 347 解題筆記:先計數,再用大小為 k 的 heap 或 bucket sort(次數當 index)取出次數最多的 k 個
LeetCode 3961: Maximize Sum of Device Ratings
·1 分鐘
LeetCode Medium Greedy Sorting Matrix
LeetCode 3961 解題筆記:除了一台以外,每台把最小的單位捐給同一台;那台的評分變成全域最小值,挑第二小值最小的那台當接收者
LeetCode 1229: Meeting Scheduler
·1 分鐘
LeetCode Medium Two-Pointers Sorting Array
LeetCode 1229 解題筆記:兩個人的時段各自排序後,跟 986 一樣用雙指針找交集;第一個長度夠的交集就是最早的時段