大家好,我是挨打的阿木木,爱好算法的前端摸鱼老。最近会频繁给大家分享我刷算法题过程中的思路和心得。如果你也是想提高逼格的摸鱼老,欢迎关注我,一起学习。
题目
75. 颜色分类
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地**对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
示例 1:
输入: nums = [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]
示例 2:
输入: nums = [2,0,1]
输出: [0,1,2]
示例 3:
输入: nums = [0]
输出: [0]
示例 4:
输入: nums = [1]
输出: [1]
提示:
n == nums.length1 <= n <= 300nums[i]为0、1或2
进阶:
- 你可以不使用代码库中的排序函数来解决这道题吗?
- 你能想出一个仅使用常数空间的一趟扫描算法吗?
思路
- 先通过一轮遍历,记录
0所在的后一个位置索引,每次遇到0, 就交换节点的位置,然后把索引值+1,这样子一轮遍历过后所有的0就能被全部交换到前面去; - 再通过一轮遍历,记录
2所在的前一个位置索引,每次遇到2, 就交换节点的位置,然后把索引值-1,这样子一轮遍历过后所有的2就能被全部交换到后面了。
实现
/**
* @param {number[]} nums
* @return {void} Do not return anything, modify nums in-place instead.
*/
var sortColors = function(nums) {
const n = nums.length;
let left = 0, right = n - 1;
// 从左到右交换节点, 把0换到最前面
for (let i = 0; i < n; i++) {
if (nums[i] === 0) {
if (i !== left) {
[ nums[i], nums[left] ] = [ nums[left], nums[i] ];
}
left++;
}
}
// 从右到左交换节点, 把2换到最后面
for (let i = n - 1; i >= 0; i--) {
if (nums[i] === 2) {
if (i !== right) {
[ nums[i], nums[right] ] = [ nums[right], nums[i] ];
}
right--;
}
}
return nums;
};
优化
翻车
我们可以把上述过程合并成同一步。常规做法我们可能会统计前面0的数量,如果找到2的时候,我们会从后面开始找到第一个不为2的元素换过去,这样子如果后面是0换回来可能会出问题,这里我先上演一波翻车事例, 这逻辑乍一看没什么毛病,实际上当最后一个元素是0的时候,会错误的把0带回来后就跳过了。
/**
* @param {number[]} nums
* @return {void} Do not return anything, modify nums in-place instead.
*/
var sortColors = function(nums) {
const n = nums.length;
let left = 0, right = n - 1;
// 从左到右交换节点, 把0换到最前面
for (let i = 0; i <= right; i++) {
if (nums[i] === 0) {
[ nums[i], nums[left] ] = [ nums[left], nums[i] ];
left++;
} else if (nums[i] === 2) {
// 万一人家原本就是2
while (nums[right] === 2) {
right--;
}
// 判断是否需要交换
if (right > i) {
[ nums[i], nums[right] ] = [ nums[right], nums[i] ];
right--;
}
}
}
return nums;
};
最终代码
/**
* @param {number[]} nums
* @return {void} Do not return anything, modify nums in-place instead.
*/
var sortColors = function(nums) {
const n = nums.length;
let left = 0, right = n - 1;
// 从左到右交换节点, 把0换到最前面
for (let i = 0; i <= right; i++) {
if (nums[i] === 2) {
// 万一人家原本就是2
while (nums[right] === 2) {
right--;
}
// 判断是否需要交换
if (right > i) {
[ nums[i], nums[right] ] = [ nums[right], nums[i] ];
right--;
}
}
if (nums[i] === 0) {
[ nums[i], nums[left] ] = [ nums[left], nums[i] ];
left++;
}
}
return nums;
};
看懂了的小伙伴可以点个关注、咱们下道题目见。如无意外以后文章都会以这种形式,有好的建议欢迎评论区留言。