继续刷LeetCode 热题 HOT 100 的题目,并且在博客更新我的solutions。在csdn博客中我会尽量用文字解释清楚,相关Java代码大家可以前往我的个人博客jinhuaiyu.com中查看。
题目:颜色分类
给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
必须在不使用库的sort函数的情况下解决这个问题。
示例 1:
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
示例 2:
输入:nums = [2,0,1]
输出:[0,1,2]
提示:
n == nums.length
1 <= n <= 300
nums[i] 为 0、1 或 2
进阶:
你可以不使用代码库中的排序函数来解决这道题吗?
你能想出一个仅使用常数空间的一趟扫描算法吗?
solution:双指针
这道题有多种方法解决,包括但不限于排序、交换、统计……排序这里就不用了,感觉脱离题目环境了;统计的思想是统计出三种颜色分别有多少,然后重新按顺序填上去,如果通过巧妙的设计也可以在常数空间一趟扫描下解决,但是如果我们把关注点放在边统计边填,倒不如直接采用交换的思想。
简而言之,就是在一趟扫描时,碰到0就放到数组最前面,碰到2就放到数组最后面。但是这样需要移动数组,增加复杂度,我们可以利用双指针left和right分别指向数组首尾,当扫描到i位置是0时,就交换nums[i]和nums[left],然后把left指针右移一位;同理,碰到2就和nums[right]交换,right左移一位。这样一来,让left指针左侧都是0,right指针右侧都是2,我们不再需要移动数组。
但以上仍有一些问题,什么时候扫描能进行到下一个?是不是每次交换,就可以进行下一个了(i++)?如果这样的话会有bug,如果是和left交换后,left和i都自增1是没有问题的,因为交换后left和i中间(包括i)都是1,1是不需要处理的;如果是和right交换,虽然把2交换到后面去了,但从后面交换过来的并不知道是什么数字(和left交换肯定是1,因为前面没被处理的都是1),所以right–后,i并不能++,我们还要继续讨论交换过来的这个数字是什么;如果扫描到1,直接进行下一个即可,不用处理。
最后,我们并不需要让i扫描到数组尾部,只需要扫描超过right指针即可,因为right指针后面确定都是2了。
Finally,带有详细注释的代码放在我的个人博客http://jinhuaiyu.com/leetcode-sort-colors/