题目描述
原地把只含 0、1、2 的数组排成 0 在前、1 在中、2 在后。
nums = [2, 0, 2, 1, 0]
输出 = [0, 0, 1, 2, 2]
思路解析
l 左边都是 0,r 右边都是 2,i 是当前位置。i 遇 0 就和 l 换,l 和 i 都前进;遇 2 就和 r 换,r 后退,但 i 不动,因为换过来的数还得再看;遇 1 就 i 直接前进。
三指针就位:三个指针就位:l 和 i 都从最左边开始,r 在最右边。i 负责逐个检查,l 和 r 负责把 0 和 2 归位。
i=0 看到 2:当前是 2,和 r(下标 4)交换,r 退到 3。注意 i 不前进——因为换过来的 0 还没检查。
i=0 看到 0:换过来是 0,和 l(下标 0)交换,l 和 i 都前进到 1。
i=1 看到 0:又是 0,和 l(下标 1)交换,l 和 i 都前进到 2。左边已经是 [0, 0] 了。
i=2 看到 2:当前是 2,和 r(下标 3)交换,r 退到 2。i 不动。
i=2 看到 1:换过来是 1,本来就该在中间,不用换,i 直接前进。此时 i 已经大于 r,扫描结束。
完成:一趟扫描完成排序:[0, 0, 1, 1, 2]。
l 在左、r 在右各守一种值,中间是待处理区——这就是分区思想,快排的 partition 也类似。
参考代码(Python)
Python
1l, r, i = 0, len(nums)-1, 0
2while 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: i += 1复杂度分析
- 时间复杂度:O(n) —— 一趟扫描
- 空间复杂度:O(1) —— 原地交换
套路模板
记住骨架: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: i += 1 # 中间值易错点
- ❌ 换到右边后 i 也前进 → ✅ 遇到 2 交换后 i 不动(从右边换来的值还没检查,可能是 0)
- ❌ while i < len(nums) → ✅ while i <= r(r 右边已排好,i 越过 r 就该停)
以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握。