LeetCode 75. 颜色分类(双指针)
一、完整代码(精简注释)
class Solution {
public:
void sortColors(vector<int>& nums) {
int p = 0, q = nums.size() - 1;
// p 左边全是 0,q 右边全是 2
int i = 0; // 遍历指针
while (i <= q) {
if (nums[i] == 2) {
swap(nums[i], nums[q]); // 将 2 放到右边
q--; // i 不动,重新检查交换过来的元素
}
else if (nums[i] == 0) {
swap(nums[i], nums[p]); // 将 0 放到左边
p++;
i++;
}
else {
i++; // 遇到 1,直接跳过
}
}
}
};
二、算法总结
1. 核心思想
采用双指针 + 遍历指针(三指针法),通过交换元素,将数组划分为三个区域:
-
左边全部是
0。 -
中间全部是
1。 -
右边全部是
2。
不需要额外数组,只需遍历一次即可完成排序。
2. 指针定义
-
p:指向下一个应该存放 0 的位置。 -
q:指向下一个应该存放 2 的位置。 -
i:遍历数组,判断当前元素。
在遍历过程中,数组分为四个区域:
| 区域 | 含义 |
|---|---|
[0, p-1] |
全部是 0 |
[p, i-1] |
全部是 1 |
[i, q] |
尚未处理的元素 |
[q+1, n-1] |
全部是 2 |
3. 指针移动规则
情况一:遇到 0
swap(nums[i], nums[p]);
p++;
i++;
将 0 交换到左边,p 和 i 同时右移。
情况二:遇到 1
i++;
1 本来就应该位于中间,因此直接跳过。
情况三:遇到 2
swap(nums[i], nums[q]);
q--;
将 2 交换到右边,q 左移,但 i 不移动。
原因:从右边交换过来的元素可能是 0、1 或 2,还没有检查过,所以需要继续判断当前位置。
4. 为什么循环条件是 i <= q?
while (i <= q)
因为:
-
i是未处理区域的起点。 -
q是未处理区域的终点。 -
当
i == q时,还有最后一个元素需要处理。 -
当
i > q时,说明所有元素都已经处理完成。
因此循环条件必须是 i <= q。
5. 最终结果
假设原数组:
nums = [2, 0, 2, 1, 1, 0]
排序完成:
nums = [0, 0, 1, 1, 2, 2]
p = 2;
q = 3;
i = 4;
最终:
-
p左边全部是 0。 -
q右边全部是 2。 -
p到q之间全部是 1(区间可能为空)。
6. 复杂度分析
时间复杂度:\(O(n)\)
每次循环至少使 i 右移或者 q 左移,因此总操作次数为 \(O(n)\)。
空间复杂度:\(O(1)\)
只使用了三个指针变量,没有额外申请数组。
三、重点记忆
这道题的核心就是三个指针的配合:
遇到 0 → 和 p 交换 → p++,i++
遇到 1 → 直接跳过 → i++
遇到 2 → 和 q 交换 → q--,i 不动
最关键的两点:
-
遇到 2 时,
i不能移动,因为交换回来的元素还没有处理。 -
循环条件是
i <= q,因为i == q时仍然有一个元素需要检查。
记住这两个细节,就能掌握这道题的双指针解法。