75. 颜色分类

中等 含交互动画

学完这道你应该能: 说清它为什么用「三指针」,而不是死记步骤; 合上页面,自己默写出核心代码; 用 30 秒把思路讲清楚。

AI 私教 · 就着本题动画讲
卡住了不用硬扛,按 8 步把这题真正走通。
从读题、试解、找法到手写代码,边问边打勾,适合第一次系统学算法的同学。

题目描述

原地把只含 0、1、2 的数组排成 0 在前、1 在中、2 在后。

nums = [2, 0, 2, 1, 1, 0]
输出 = [0, 0, 1, 1, 2, 2]

思路解析

最直接是「计数排序」:先扫一遍数出 0、1、2 各几个,再扫一遍按数量重新填回去。能对,但要扫两趟。题目只有三种值,其实一趟就能就地分好——这正是三指针的用武之地。

关键招:l 左边全是已就位的 0,r 右边全是已就位的 2,i 是当前检查位。i 遇 0 就和 l 换、l 和 i 都前进;遇 2 就和 r 换、r 后退但 i 不动(换来的数还没看过);遇 1 就 i 直接前进。i 越过 r 就排完——一趟、原地。

三指针就位:三个指针就位:li 都从最左边开始,r 在最右边。i 负责逐个检查,l 和 r 负责把 0 和 2 归位。

i=0 看到 2 → 换到右边:负例分支:当前是 2,和 r(下标 5)交换,r 退到 4。注意 i 不前进——换过来的是个 0,还没检查过,下一步还得在原地重判它。

i=0 重判 · 看到 0 → 换到左边:重判 i=0:换过来的正是 0,和 l(下标 0)交换(其实是自己换自己),l 和 i 都前进到 1。下标 0 已锁定为 0(变灰)。

i=1 看到 0 → 换到左边:又是 0,和 l(下标 1)交换,l 和 i 都前进到 2。左边已是 [0, 0] 两个就位的红色。

i=2 看到 2 → 换到右边:当前是 2,和 r(下标 4)交换,r 退到 3。i 仍不动,换来的 1 还得再判。

i=2 重判 · 看到 1 → 前进:重判 i=2:换过来是 1,本来就该在中间,不交换,i 直接前进到 3。

i=3 看到 1 → 前进越过 r:又是 1,不交换,i 前进到 4。此时 i 已越过 r(4 大于 3),r 右边的 [2, 2] 早已就位,扫描结束。

完成:一趟扫描完成排序:l 左边全 0、r 右边全 2、中间全 1,得到 [0, 0, 1, 1, 2, 2]

l 守左、r 守右各管一种值,中间 [l, i) 是已确定的 1、[i, r] 是待处理区——这就是分区思想,快排的 partition 也是同一招。

参考代码(Python)

Python
1l, r, i = 0, len(nums) - 1, 0
2while i <= r:                      # i 越过 r 就停
3    if nums[i] == 0:               # 该放左段
4        nums[l], nums[i] = nums[i], nums[l]; l += 1; i += 1
5    elif nums[i] == 2:            # 该放右段
6        nums[r], nums[i] = nums[i], nums[r]; r -= 1   # i 不动!
7    else:                         # 是 1,留中间
8        i += 1

复杂度分析

  • 时间复杂度:O(n) —— i 和 r 相向而行,最多扫一趟 n 个元素
  • 空间复杂度:O(1) —— 只用 l、i、r 三个下标,原地交换、不开计数数组

套路模板

记住骨架:l 守左、r 守右、i 扫描;换到右边后 i 不动。改一下条件就能复用:快排 partition、把某元素全移到末尾都是这套分区。

Python
1# 例: 0 归左、2 归右、1 留中(荷兰国旗)
2l, i, r = 0, 0, n - 1
3while i <= r:
4    if nums[i] == 0:                  # 该放左
5        nums[i], nums[l] = nums[l], nums[i]; l += 1; i += 1
6    elif nums[i] == 2:               # 该放右
7        nums[i], nums[r] = nums[r], nums[i]; r -= 1   # i 不动!
8    else:
9        i += 1                       # 中间值,直接走

易错点

  • 错误写法:遇到 2 和 r 交换后 i 也一起前进正确写法:遇到 2 交换后只 r 后退,i 留在原地重判(从 r 换来的数还没检查过,可能是 0(本例 i=0 换来的就是 0),i 一前进就漏判,那个 0 永远到不了左段)
  • 错误写法:while i < len(nums)正确写法:while i <= r(r 右边已全是就位的 2,i 越过 r 就该停,再扫只会把排好的 2 又换乱)
  • 错误写法:遇到 1 也做一次交换正确写法:遇到 1 只 i += 1,不交换(1 本就该在中段,多余交换不出错但白费动作,逻辑上也容易写乱 l/r)

以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握

下一题 →135. 分发糖果 ← 返回题库