[LeetCode75题颜色分类] | 刷题打卡

420 阅读4分钟

题目介绍

这道题是LeetCode75. 颜色分类,难度medium

给定一个包含红色、白色和蓝色,一共 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.length
  • 1 <= n <= 300
  • nums[i] 为 0、1 或 2  

进阶:

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

解题思路

根据题目的要求,就是将给定数组nums按照0, 1, 2的顺序排序后返回,要仔细审题,注意这里是原地对它们进行排序,所以不要上来就是循环nums,然后将0, 1, 2依次添加到新数组之后返回,这样是不符合要求的。

原地那就是让我们来用指针的方法交换数组了。

单指针

设定一个指针ptr = 0,然后先循环nums数组,找到每个0ptr交换,每交换一次ptr++,循环结束后nums数组的头部都是0

然后从ptr开始再次循环nums数组(因为ptr之前的都是0),找个每个1ptr交换,没交换一次ptr++,循环结束后的nums数组就是我们想要的结果数组了。

时间复杂度:O(n)O(n),其中 nn 是数组 \textit{nums}nums 的长度。

空间复杂度:O(1)O(1)。

双指针

单指针是用一个指针ptr循环两次,那我们可不可以只循环一次呢?

当然可以,设定两个指针p0 = 0, p1 = 0,然后循环nums数组,如果遇见0的话我们将ip0交换,注意这里,因为我们是按照0, 1, 2的顺序来排序的,所以0之后都是1,这里我们可能会把1交换出去,所以这里当p0 < p1的时候,我们还需要将ip1交换,然后p0++; p1++

如果我们遇见1,那么就跟单指针的方法一样,将p1i进行交换,然后p1++;

这样最后我们就得到了我们想要的数组。

左右双指针

上一步中我们是用双指针从左到右遍历nums数组的,我们也可以从两端开始遍历,设定left = 0right = nums.length - 1,当从左到右超过了right的时候就可以停止遍历了。

当遇见0的时候,ileft交换,然后left++

当遇见2的时候,iright交换,然后right--

1保持不变,遍历结束后的数组nums就是我们需要的结果数组了。

解题代码

单指针

var sortColors = function(nums) {
  const n = nums.length;
  let ptr = 0;
  for (let i = 0; i < n; i++) {
    if (nums[i] === 0) {
      [nums[i], nums[ptr]] = [nums[ptr], nums[i]];
      ptr++;
    }
  }
  for (let i = ptr; i < n; i++) {
    if (nums[i] === 1) {
      [nums[i], nums[ptr]] = [nums[ptr], nums[i]];
      ptr++;
    }
  }
  return nums;
}

双指针

var sortColors1 = function(nums) {
  const n = nums.length;
  let p0 = 0, p1 = 0;
  for (let i = 0; i < n; i++) {
    if (nums[i] === 1) {
      [nums[i], nums[p1]] = [nums[p1], nums[i]];
      p1++;
    } else if (nums[i] === 0) {
      [nums[i], nums[p0]] = [nums[p0], nums[i]];
      if (p0 < p1) {
        [nums[i], nums[p1]] = [nums[p1], nums[i]];
      }
      p0++;
      p1++;
    }
  }
  return nums;
}

两端双指针

var sortColors2 = function(nums) {
  const n = nums.length;
  let left = 0, right = n - 1;
  let i = 0;
  while(i <= right) {
    while(i <= right && nums[i] === 2) {
      [nums[i], nums[right]] = [nums[right], nums[i]];
      right--;
    }
    if (nums[i] === 0) {
      [nums[i], nums[left]] = [nums[left], nums[i]];
      left++;
    }
    i++;
  }
  return nums;
}

刷题打卡记录

这里是之前的刷题打卡记录,大家有兴趣的可以看下,如果有什么不同的见解和看法或者觉得有什么错误的,欢迎在评论区留言!🙏🙏🙏

[LeetCode0303题区域和检索 - 数组不可变] | 刷题打卡

[LeetCode1200. 最小绝对差] | 刷题打卡

[LeetCode0304题二维区域和检索 - 矩阵不可变] | 刷题打卡

[LeetCode11题盛最多水的容器] | 刷题打卡

[LeetCode0338题比特位计数] | 刷题打卡

[LeetCode209题长度最小的子数组] | 刷题打卡

[LeetCode236题二叉树的最近公共祖先] | 刷题打卡

[LeetCode141题环形链表] | 刷题打卡

[LeetCode53题最大子序和] | 刷题打卡

[LeetCode48题旋转图像] | 刷题打卡

[LeetCode155题最小栈] | 刷题打卡

[LeetCode1124题表现良好的最长时间段] | 刷题打卡

[LeetCode274题H指数] | 刷题打卡

[LeetCode367题有效的完全平方数] | 刷题打卡

[LeetCode1047题删除字符串中的所有相邻重复项] | 刷题打卡

[LeetCode160题相交链表] | 刷题打卡

[LeetCode1438题绝对差不超过限制的最长连续子数组] | 刷题打卡

[LeetCode434题字符串中的单词数] | 刷题打卡

总结

加油!hxdm!!!💪💪💪

本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情