1. 题目链接
LeetCode 75. 颜色分类
2. 题目描述
给定一个包含红色(0)、白色(1)和蓝色(2)的数组 nums,要求原地对数组进行排序,使得相同颜色的元素相邻,且按红、白、蓝顺序排列。
要求:
- 仅使用常数空间。
- 仅遍历数组一次。
示例:
- 输入:
nums = [2,0,2,1,1,0]→ 输出:[0,0,1,1,2,2] - 输入:
nums = [2,2,1,0]→ 输出:[0,1,2,2]
3. 示例分析
- 常规案例:
- 输入:
[2,0,2,1,1,0]- 通过交换将
0移动到左侧,2移动到右侧,中间保留1。 - 最终结果:
[0,0,1,1,2,2]。
- 通过交换将
- 输入:
- 边界案例:
- 输入全为
0:[0,0,0]→ 无需操作,直接返回。 - 输入全为
2:[2,2,2]→ 所有元素交换到右侧。 - 输入全为
1:[1,1,1]→ 无需操作。
- 输入全为
4. 算法思路
核心思想:三指针法
- 指针定义:
left:指向已处理0的右边界(初始为-1)。right:指向已处理2的左边界(初始为n)。i:遍历指针,从0开始。
- 遍历规则:
nums[i] == 0:- 交换
nums[++left]和nums[i],i++。 - 将
0移动到left右侧,扩展0的区域。
- 交换
nums[i] == 1:i++。1保留在中间区域,无需处理。
nums[i] == 2:- 交换
nums[--right]和nums[i]。 - 将
2移动到right左侧,扩展2的区域。 - 不移动
i:交换后的nums[i]可能是0或1,需再次检查。
- 交换
- 终止条件:
- 当
i >= right时,所有元素已处理完毕。
- 当
5. 边界条件与注意事项
- 全
0/1/2数组:- 全
0或1:遍历后指针无需移动,直接返回。 - 全
2:i不移动,通过交换将所有元素移动到右侧。
- 全
- 单元素数组:
- 无需操作,直接返回。
- 元素交换顺序:
- 交换
2时,i不自增,确保新元素被检查。
- 交换
- 时间复杂度:
- 每个元素最多被交换两次,时间复杂度为 O(n)。
6. 代码实现
class Solution {
public:
void sortColors(vector<int>& nums) {
int n = nums.size();
int left = -1, right = n, i = 0;
while (i < right) {
if (nums[i] == 0) {
swap(nums[++left], nums[i++]); // 交换0到左区,i前进
} else if (nums[i] == 1) {
i++; // 保留中间区
} else {
swap(nums[--right], nums[i]); // 交换2到右区,i不前进
}
}
}
};

关键步骤解析
-
初始化指针:
int left = -1, right = n, i = 0;left初始指向0区的左边界(无元素),right指向2区的右边界。
-
处理
0的逻辑:swap(nums[++left], nums[i++]);left向右扩展,将0交换到0区末尾,i前进。
-
处理
2的逻辑:swap(nums[--right], nums[i]);right向左扩展,将2交换到2区头部,i不前进。
与其他解法的对比
| 方法 | 时间复杂度 | 空间复杂度 | 核心思想 |
|---|---|---|---|
| 三指针法 | O(n) | O(1) | 分区交换,一次遍历 |
| 计数排序法 | O(n) | O(1) | 统计频次,重新填充数组 |
| 双指针法 | O(n) | O(1) | 两次遍历,先排0再排1 |
总结
三指针法通过一次遍历实现原地排序,是解决荷兰国旗问题的经典方案。其核心在于 分区处理 和 指针动态调整,以线性时间高效完成排序。
优化点:
- 合并部分条件判断,减少代码行数。
- 预判全
1情况,提前终止遍历。
适用场景:
- 需要严格满足“一次遍历”和“常数空间”要求的场景。
- 大规模数据排序(
n ≤ 1e5)。
关键点:
- 理解
i与right的协作逻辑。 - 处理
0和2时的指针移动差异。



















