领扣网算法学习笔记
本系列的算法题目来自领扣网
数组类算法第五天
题目:颜色分类
给定一个包含红色、白色和蓝色,一共 *n *个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
示例:
输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]
说明:
- 进阶:
- 一个直观的解决方案是使用计数排序的两趟扫描算法。
首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。 - 你能想出一个仅使用常数空间的一趟扫描算法吗?
- 一个直观的解决方案是使用计数排序的两趟扫描算法。
解题过程:
思路:
看到这题的第一个想法就是说明中的方案:我把个数记下来就好了,管你顺序是啥,看到进阶后放弃了这种做法,然后想法是:定义两个变量用来记录红色和蓝色的位置,然后遇到红色往前插入,其余球往后移一个,遇到蓝色,则往后插入,其余球往前移动一个,但是这样做的话,移动球的代价太大,直接否决,
然后我想的是:我不移动球,因为颜色已经确定,顺序页已经确定,我交换位置,
- 定义两个变量分别记录红蓝的位置,初始值:红球为0,篮球为数组的长度,位与最后。
- 当判断到一个蓝球时,与记录篮球的位置交换,然后篮球位置往前移一位,同时因为互换了球,所以交换的位置需要重新判断,
- 当判断是白球时,白球位置加一,然后继续判断,原因是红球往前,蓝球往后,白球放中间,位置不用变。
- 当判断是红球时,将红球位置编程红球,此时如果不存在白球,那么对白球无影响,那么红球与白球的位置相同,两者位置都往后移一个,如果存在白球,则红球位置将移到白球上,需要补一个给白球,此时补得位置刚好是判断位置,所以直接将判断位置变成白色即可。
代码如下:
class Solution {
public void sortColors(int[] nums) {
if((nums == null && nums.length == 0) || nums.length == 1){
return ;
}
int blueNum = nums.length; // 记录蓝色的位置,同时兼顾循环次数的身份
int redNum = 0; // 记录红色的位置
for(int i=0;i<blueNum;i++){ // 循环数组
if(nums[i] == 2){ // 判断是否为蓝色
blueNum--; // 记录的蓝色位置-1
nums[i] = nums[blueNum]; // 因为数组是从0开始,所以这里-1之后刚好是末尾的值
// 将蓝色位置的数据赋给当前位置
nums[blueNum] = 2; // 将蓝色位置变成蓝色
i--; // 交换后需要重新判断,所以将判断的位置退后一个
} // 白色不用管,所以不判断
else if(nums[i] == 0){ // 判断是否为红色
if(redNum != i){ // 判断是否存在白球,如果存在,则红色数量不等于当前判断的数量
// 此时当前判断的位置等于已知的红色数量加已知的白球数量
nums[i] = 1; // 存在白球则将当前位置变为白色
}
nums[redNum] = 0; // 因为无论存不存在白色,都需要将红色位置变成红色
redNum++; // 然后红色位置往后移一位,所以直接拿出来
}
}
}
}
// 用时小于1ms
后续思考+总结:
该题不打算做高质量的分析,感觉这个是最佳的思路了,还有一个就是判断完是蓝色后不使用else,然后直接再判断交换过来的是红白色,但是效率如何,未知。感觉速度不会提升。
其实解决问题的方法很多,找到最好的就需要好好思考了。