LeetCode98 - 颜色分类
📝 题目描述
题目链接:颜色分类
给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
必须在不使用库内置的 sort 函数的情况下解决这个问题。
示例:
1 |
|
提示:
n == nums.length1 <= n <= 300nums[i] 为 0、1 或 2
💡 解题思路
方法一:计数排序
计数排序,很直观、很容易想到的方法。反正就三个数字“0、1、2”,最后无非就是给这三个数字排序。因此我们可以先扫描一遍数组,统计一下有多少个“0、1、2”,然后按照统计结果,原地将他们写进数组。
方法二:三指针
用三个指针(left、i、right)维护四个区域:
| 下标范围 | 含义 |
|---|---|
[0, left) |
已经放好的 0 |
[left, i) |
已经放好的 1 |
[i, right] |
尚未处理的元素 |
[right + 1, n) |
已经放好的 2 |
这里 [a, b) 表示包含 a,不包含 b。每次只检查未知区域最左端的 nums[i]。
具体操作是,我们初始化 left = 0 表示下一个0应该放的位置,i = 0 表示下一个1该放的位置,right = nums.size() - 1 表示下一个2该放的位置,然后从左往右扫描:
- 如果
nums[i] == 0,则将nums[left]与nums[i]交换,然后left与i都向右移动; - 如果
nums[i] == 1,则啥也不用做,i向右移动; - 如果
nums[i] == 2,则将nums[right]与nums[i]交换,然后right向左移动。
需要注意的是,遇到 2 时,不能 ++i(向右移动),因为 right 位置也属于未知区域,交换过来的数可能是 0、1 或 2,必须重新检查。
例如数组 [1, 2, 0],当 i = 1 时,把 2 和末尾的 0 交换:
1 | [1, 2, 0] → [1, 0, 2] |
此时必须继续处理 i 位置的 0。如果直接 ++i,循环就会结束,结果仍然无序。
遇到 0 时,可以 ++i,根据区域定义:
- 当
left < i时,nums[left]一定是已经确认过的 1;交换后,i位置是 1,可以直接跳过; - 当
left == i时,只是自己和自己交换,这个 0 已经就位,也可以继续前进。
因此,虽然 i 有时不动,这仍然是一趟扫描。 未知区域的长度是 right - i + 1,每轮都会通过 ++i 或 --right 缩小一个位置,因此恰好处理 n 次.
🔧 代码实现
1、计数排序
1 | class Solution { |
2、三指针
1 | class Solution { |
📊 复杂度分析
1、计数排序
- 时间复杂度:,先统计,再回填, 仍然是 。
- 空间复杂度:,只用了固定数量的变量,也符合原地修改的要求。
2、三指针
- 时间复杂度:,只需要一趟扫描。
- 空间复杂度:,只用了常数个变量。
🎯 总结
- 核心思想:技巧类题目,三指针的思路类似于前面的题目“移动零”。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 很多时候不懂事!