給定包含 0、1、2 的陣列,原地排序使相同顏色相鄰,順序為 0(紅)、1(白)、2(藍)。即荷蘭國旗問題。
範例:[2,0,2,1,1,0] → [0,0,1,1,2,2]
void sortColors(vector& nums) {
int cnt[3] = {0};
for (int x : nums) ++cnt[x];
int idx = 0;
for (int c = 0; c < 3; ++c)
while (cnt[c]--) nums[idx++] = c;
}
維護三個區間:[0, p0) 全 0,[p0, i) 全 1,(p2, n-1] 全 2,[i, p2] 待處理。
void sortColors(vector& nums) {
int p0 = 0, p2 = nums.size() - 1, i = 0;
while (i <= p2) {
if (nums[i] == 0) {
swap(nums[i++], nums[p0++]); // 0 交換到左區,i/p0 後移
} else if (nums[i] == 2) {
swap(nums[i], nums[p2--]); // 2 交換到右區,p2 前移,i 不動(新換來的需檢查)
} else {
++i; // 1 保持中間,i 後移
}
}
}
void sortColors(vector& nums) {
vector v0, v1, v2;
for (int n : nums) {
if (n == 0) v0.push_back(0);
else if (n == 1) v1.push_back(1);
else v2.push_back(2);
}
nums.clear();
nums.insert(nums.end(), v0.begin(), v0.end());
nums.insert(nums.end(), v1.begin(), v1.end());
nums.insert(nums.end(), v2.begin(), v2.end());
}
nums = [2,0,2,1,1,0]
| 步驟 | i | p0 | p2 | 操作 | 陣列狀態 |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 5 | nums[0]=2 ↔ nums[5]=0 | [0,0,2,1,1,2] |
| 1 | 0 | 1 | 5 | nums[0]=0 → i++,p0++ | [0,0,2,1,1,2] |
| 2 | 1 | 1 | 5 | nums[1]=0 → swap(1,1), i++,p0++ | [0,0,2,1,1,2] |
| 3 | 2 | 2 | 5 | nums[2]=2 ↔ nums[5]=2, p2-- | [0,0,2,1,1,2] |
| 4 | 2 | 2 | 4 | nums[2]=2 ↔ nums[4]=1, p2-- | [0,0,1,1,2,2] |
| 5 | 2 | 2 | 3 | nums[2]=1 → i++ | [0,0,1,1,2,2] |
| 6 | 3 | 2 | 3 | nums[3]=1 → i++ | [0,0,1,1,2,2] |
| 7 | 4 | 2 | 3 | i > p2 結束 | [0,0,1,1,2,2] |
| 解法 | 時間 | 空間 | 遍歷次數 | 原地 |
|---|---|---|---|---|
| 計數排序 | O(n) | O(1) | 2 | 是 |
| 三指針 | O(n) | O(1) | 1 | 是 |
| range-for | O(n) | O(n) | 1 | 否 |
LeetCode 75 是經典荷蘭國旗問題。三指針單遍掃描為最優解法,時間 O(n)、空間 O(1)、原地排序。計數排序代碼更簡潔,兩遍掃描工程上可接受。range-for 版本雖直觀但需額外空間,不符合原地要求。掌握三指針不變量設計是解決分區類問題的核心技能。