「这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战」。
题目
链接:leetcode-cn.com/problems/so…
给定一个包含红色、白色和蓝色,一共 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
进阶:
- 你可以不使用代码库中的排序函数来解决这道题吗?
- 你能想出一个仅使用常数空间的一趟扫描算法吗?
解题思路
思路1
建立双指针,一个从头到尾,一个从尾到头。 从头到尾遍历数组,如果当前值(nums[i])一直为2则一直让尾指针从后往前和当前值交换:将2放到数组最后面。直到当前值不为2。 当前值为0的时候和头指针互换,将0放到数组最前面。
/**
* @param {number[]} nums
* @return {void} Do not return anything, modify nums in-place instead.
*/
var sortColors = function(nums) {
let p0 = 0;
let p2 = nums.length-1;
for(let i=0;i<=p2;i++){
// 找到2 和尾指针互换
while(nums[i] === 2 && i<p2){
let temp = nums[i];
nums[i] = nums[p2];
nums[p2] = temp;
p2--;
}
// 找到0和头指针互换,这里可以剪枝判断当p0 == i 的时候continue
if(nums[i] === 0){
let temp = nums[i];
nums[i] = nums[p0];
nums[p0] = temp;
p0++;
}
}
};
思路2
1.解析输入为几个开始和结束的坐标数组,比如 [1,1,0,0] 就是
let orderMap = {
0: [0, 1],
1: [2, 3]
}
2.循环输入的数组,的索引在不在坐标范围内
3.如果不在坐标范围内,交换在范围内,不是正确的值的位置
代码
/**
* @param {number[]} nums
* @return {void} Do not return anything, modify nums in-place instead.
*/
function rightOrder(nums, orderMap) {
for (let i = 0; i < nums.length; i++) {
let val = nums[i],
order = orderMap[val];
if (i < order[0] || i > order[1]) {
return i;
}
}
return false;
}
function changeOrder(nums, orderMap, index) {
let val = nums[index];
let order = orderMap[val];
let [start, end] = order;
for (let a = start; a <= end; a++) {
if (nums[a] !== val) {
([nums[index], nums[a]] = [nums[a], nums[index]]);
break;
}
}
}
function sortColors(nums) {
let map = {},
len = nums.length;
for (let i = 0; i < len; i++) {
let e = nums[i];
map[e] = map[e] ? map[e] + 1 : 1;
}
let orderMap = {},
before = 0;
for (let key in map) {
let val = map[key],
end = before + val - 1;
orderMap[key] = [before, end];
before = end + 1;
}
let index = rightOrder(nums, orderMap);
while (index !== false) {
changeOrder(nums, orderMap, index);
index = rightOrder(nums, orderMap)
}
return nums;
}
其他思路
- 冒泡排序
- 操作数组
- 三指针:关于nums[curr]===0的时候为什么可以自信的直接加1而不检测被调换过来的数字呢?我们可以假设被换过来的数字可能为1,2;但是如果这个数字是2,那么在之前的循环中已经被移动到末尾了,所以这个数组只可能是1.
/**
* @param {number[]} nums
* @return {void} Do not return anything, modify nums in-place instead.
*/
// 冒泡排序 76ms
// var sortColors = function(nums) {
// for(let i=0;i<nums.length;i++){
// for(j=0;j<nums.length-i;j++){
// if(nums[j]>nums[j+1]){
// [nums[j],nums[j+1]]=[nums[j+1],nums[j]];
// }
// }
// }
// return nums
// };
// 操作数组 64ms
/* 0,1,2 排序。一次遍历,如果是0,则移动到表头,如果是2,则移动到表尾,不用考虑1。 */
// var sortColors = function(nums) {
// let i=0,count=0;//count表示循环次数
// while(count<nums.length){
// if(nums[i]===0){
// nums.splice(i,1);
// nums.unshift(0);
// i++;
// }else if(nums[i]===2){
// nums.splice(i,1);
// nums.push(2);
// // 这边i不加1是因为删掉这个元素之后,后一个元素会自动顶上来
// }else{
// i++;
// }
// count++;
// }
// return nums
// };
// 官方题解的三指针解法 76ms
// left:0的最右边界,right:2的最左边界,curr当前下标
var sortColors = function(nums) {
let left=0,right=nums.length-1,curr=0;
while(curr<=right){
if(nums[curr]===0){
[nums[curr],nums[left]]=[nums[left],nums[curr]];
//这里为什么可以自信的直接加1而不检测被调换过来的数字呢?我们可以假设被换过来的数字可能为1,2;但是如果这个数字是2,那么在之前的循环中已经被移动末尾了,所以这个数组只可能是1.
left++;
curr++;
continue;
}
if(nums[curr]===2){
[nums[curr],nums[right]]=[nums[right],nums[curr]];
right--;
continue;
}
if(nums[curr]===1){
curr++;
continue;
}
}
return nums
};
sortColors([2,0,1])