「前端刷题」75. 颜色分类

128 阅读2分钟

「这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战」。

题目

链接:leetcode-cn.com/problems/so…

给定一个包含红色、白色和蓝色,一共 n 个元素的数组,**原地**对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

此题中,我们使用整数 0、 12 分别表示红色、白色和蓝色。

 

示例 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.length
  • 1 <= n <= 300
  • nums[i]012

 

进阶:

  • 你可以不使用代码库中的排序函数来解决这道题吗?
  • 你能想出一个仅使用常数空间的一趟扫描算法吗?

解题思路

思路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.如果不在坐标范围内,交换在范围内,不是正确的值的位置

image.png

代码

/**
 * @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;
      }

其他思路

  1. 冒泡排序
  2. 操作数组
  3. 三指针:关于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])