刷题心得:荷兰国旗问题与三指针法题目背景
前言
最近在刷 LeetCode 时遇到了经典的 "荷兰国旗问题"(LeetCode 75 题:颜色分类)。
题目要求:
原地对包含 0、1、2 的数组进行排序,
使得相同颜色的元素相邻,
并按照红 (0)、白 (1)、蓝 (2) 的顺序排列,
且不能使用内置的 sort 函数。
前期思考
我想着遇到数组的排序问题,首先是得先遍历一次数组,用三个变量统计0 1 2的个数,再通过循环遍历进行数组的重写。
但很显然这种办法是很笨的,时间复杂度是 O (n),空间复杂度是 O (1),虽然能 AC,但显然不是最优解
因此在询问大佬,通过大佬的思路下知道了只使用常数空间的一趟扫描算法
因此得出了要使用巧妙的指针。
理解三指针法:荷兰国旗问题的精髓
查阅资料后,我了解到荷兰国旗问题的经典解法 ——三指针法。
这种方法通过维护三个指针,将数组分为四个区域:
- 0 区域:[0, left)
- 1 区域:[left, curr)
- 未处理区域:[curr, right]
- 2 区域:(right, n-1]
具体步骤如下:
初始化 left=0,curr=0,right=n-1 当 curr <= right 时:
若 nums [curr] == 0,交换 nums [curr] 和 nums [left],left++,curr++
若 nums [curr] == 1,curr++
若 nums [curr] == 2,交换 nums [curr] 和 nums [right],right-- 这里的关键在于,
当 nums [curr] == 2 时,交换后 curr 不移动,
因为交换过来的元素可能是 0 或 2,需要再次检查。
六个人的排队逻辑
假设六个人的衣服颜色顺序为:
红、蓝、红、白、蓝、白
对应数组:nums = [0, 2, 0, 1, 2, 1]
(0 = 红,1 = 白,2 = 蓝)
目标:将队伍排成 红→白→蓝(0→1→2)。
三指针法的 “排队指挥” 过程
步骤 1:curr 指向第 1 人(红,0)
操作:红队需要站左边,让第 1 人(红)和left
指向的人(自己)交换(相当于不动)。
结果:left
右移到位置 1,curr
右移到位置 1。
当前队列:[0, 2, 0, 1, 2, 1]
指针状态:left=1
, curr=1
, right=5
。
步骤 2:curr 指向第 2 人(蓝,2)
操作:蓝队需要站右边,让第 2 人(蓝)和right
指向的第 6 人(白,1)交换。交换后队列:[0, 1, 0, 2, 2, 2]
(第 2 人和第 6 人交换)。
结果:right
左移到位置 4,curr不移动
(因为交换来的第 2 人现在是白,需要重新检查)。
当前队列:[0, 1, 0, 2, 2, 2]
指针状态:left=1
, curr=1
, right=4
。
步骤 3:curr 仍指向第 2 人(白,1)
操作:白队(1)应在中间,无需移动,直接让curr
右移到位置 2。
当前队列:[0, 1, 0, 2, 2, 2]
指针状态:left=1
, curr=2
, right=4
。
步骤 4:curr 指向第 3 人(红,0)
操作:红队需要站左边,让第 3 人(红)和left
指向的第 2 人(白,1)交换。交换后队列:[0, 0, 1, 2, 2, 2]
。
结果:left
右移到位置 2,curr
右移到位置 3。
当前队列:[0, 0, 1, 2, 2, 2]
指针状态:left=2
, curr=3
, right=4
。
步骤 5:curr 指向第 4 人(蓝,2)
操作:蓝队需要站右边,让第 4 人(蓝)和right
指向的第 5 人(蓝,2)交换(相当于不动)。
结果:right
左移到位置 3,curr不移动
(因为交换来的还是蓝,需要重新检查)。
当前队列:[0, 0, 1, 2, 2, 2]
指针状态:left=2
, curr=3
, right=3
。
步骤 6:curr 指向第 4 人(蓝,2),此时curr == right
操作:蓝队已在右边,right
左移到位置 2,循环结束(curr=3 > right=2
)。
最终队列:[0, 0, 1, 2, 2, 2]
,符合红→白→蓝的顺序!
实现该操作的代码:
while(mid <= right){if(nums[mid]== 0){int temp = 0;temp = nums[left];nums[left] = nums[mid];nums[mid] = temp;left++;mid++; }else if(nums[mid]==2){int temp = 0;temp = nums[right];nums[right] = nums[mid];nums[mid] = temp;right--;}else{mid++;}}
总结与收获
通过解决荷兰国旗问题,我有以下几点收获:
- 对于原地排序问题,指针操作是关键,合理设计指针可以大幅提高效率。
- 当问题涉及多个区域时,多指针法是一个值得尝试的方向。
- 处理指针问题时,一定要明确每个指针的含义和边界条件,避免越界和逻辑错误。
- 刷题不仅要追求 AC,还要深入理解解法背后的思想,做到举一反三。