解題說明
Sort Colors
題目描述:給定一個只包含 0、1、2 的整數陣列(代表紅、白、藍三色),請在不使用內建排序函式的情況下,就地(in-place)排序,使得相同顏色相鄰,且顏色順序為 0、1、2。
解題思路:
這道題又稱「荷蘭國旗問題」(Dutch National Flag Problem),由電腦科學家 Dijkstra 提出。樸素做法是計數排序(兩次遍歷),但最優解是一次遍歷、三指標法。
三指標(Three-Pointer)法:
維護三個指標:
low:下一個 0 應該放置的位置(初始為 0)mid:當前正在處理的元素(初始為 0)high:下一個 2 應該放置的位置(初始為 n-1)
不變量(Loop Invariant):
[0, low)全部是 0[low, mid)全部是 1(high, n-1]全部是 2[mid, high]是尚未處理的區域
處理邏輯(當 mid <= high 時):
nums[mid] == 0:與nums[low]交換,low++、mid++(因為 low 位置換來的必然是已處理過的 1)nums[mid] == 1:已在正確位置,mid++nums[mid] == 2:與nums[high]交換,high--(mid 不移動,因為換來的元素尚未檢查)
整個過程只需一次掃描,每個元素最多被交換一次,效率極高。
C++ 解法
複雜度分析
時間複雜度:O(N) — 只需一次遍歷。mid 指標從左向右移動,high 指標從右向左移動,兩者相遇即停止,總操作次數不超過 N 次。
空間複雜度:O(1) — 只使用三個指標變數,就地排序,不需要額外的陣列空間。
虛擬碼
1. Initialize low = 0, mid = 0, high = n - 1
2. While mid <= high:
a. If nums[mid] == 0:
- Swap nums[low] and nums[mid]
- Increment both low and mid
b. Else if nums[mid] == 1:
- Increment mid only
c. Else (nums[mid] == 2):
- Swap nums[mid] and nums[high]
- Decrement high only (do NOT increment mid)
3. Array is now sorted in-place其他解法
計數排序 O(N),兩次遍歷:第一次遍歷統計 0、1、2 各自的數量,第二次遍歷依照計數重新填入陣列。這個方法需要兩次遍歷,但邏輯非常簡單,不容易出錯,在面試中作為「初步解法」說明後再優化到三指標法是常見策略。
STL sort O(N log N):直接呼叫 sort(nums.begin(), nums.end())。雖然正確,但題目明確要求不使用內建排序,且時間複雜度也不如線性方法優。此方法不符合題目要求,僅作為對比參考。
延伸思考
- 如果陣列中有 k 種不同的顏色(0 到 k-1),你會如何將三指標法推廣到 k 色版本?
- 若陣列是唯讀的(read-only),只能輸出到新陣列,計數排序是否更適合?為什麼?
- 這個三指標技巧的「不變量維護」思想,可以應用到哪些其他排序或分割問題上?