这一题的链接如下:
75. 颜色分类
有一个人数组只包含 0,1,2,写一个算法,将这个原数组直接排序
这一题的思路有很多,常见的思路是:
- 排序
- 双指针
排序
直接使用常见的排序算法就可以了,下面主要说第二种
双指针
可以想象这样一个场景,对一个数组进行遍历。
如果遇到的数字是 0,则将其放到左边,如果遇到的是 2,则将其移动到右边。
当思路有了以后,就可以考虑实现了,首先可以定义两个下标,分别记为 left、right。
每一次遇到 0 的时候,就将下标为 left 桶赋值为 0,然后 left++
每一次遇到 2 的时候,就将下标为 right 桶赋值为 2,然后 right–
所以就可以写出如下的代码:
for(int i = 0; i <= right; i++){
if(nums[i] == 2 && i <= right){
swap(nums,i,right);
right--;
}
if(nums[i] == 0 ){
swap(nums,left,i);
left++;
}
}
这样的写法有一个问题,考虑如下数组:
[1,2,0],left = 0,right = 2;
第一次循环,此时判断 nums[0] = 1,因此什么都不用做
第二次循环,此时判断 nums[1] = 2, 所以发生交换,因为right = 2,所以交换后:[1,0,2]
但是这个答案明显是错误的,造成这个问题产生的原因是 right 被错误的换掉了,可以这样理解,i 在每一次的循环都是会 ++ 的,而一旦 right 是几个连续的 2, 就会有问题
[1,2,X,X,X,2,2,2]
假设此时 i = 1,right = 6,因为 nums[1] = 2, 所以此时的数组就是 还是不变,但是因为 i 增加了,所以此时 下标为 1 的那个桶就是错误的数据了。
修复
修复的方案也很简单,直接循环到 right 不等于 2 的地方即可
int left = 0;
int right = nums.length - 1;
for(int i = 0; i <= right; i++){
while(nums[i] == 2 && i <= right){
swap(nums,i,right);
right--;
}
if(nums[i] == 0 ){
swap(nums,left,i);
left++;
}
}
思考
为什么左边就不需要进行循环呢?
出现这个问题的原因在于 i 是增加的,而且在遍历的过程中,已经将所有的 0 都处理了
[0,0,0,0,0]
[0,1,0,0,0]
[1,0,0,0,0,]
以上都不会出现问题