LeetCode 👉 HOT 100 👉 颜色分类 - 中等题

793 阅读3分钟

「这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战」。

题目

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

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

示例1

输入:nums = [2,0,2,1,1,0]

输出:[0,0,1,1,2,2]

示例2

输入:nums = [2,0,1]

输出:[0,1,2]

思路

猛地一看,这道题好像就是个简单的排序问题,题目唯一要求的就是原地排序,就是要在传进来的数组上做变更,而不能以返回一个新变量的方式。

对数组进行排序,很容易就会想到之前常说的冒泡排序法:

冒泡排序代码

    /**
     * @param {number[]} nums
     * @return {void} Do not return anything, modify nums in-place instead.
     */
    var sortColors = function (nums) {
        const n = nums.length;

        if(n < 2) return;

        let temp;
        for(let i = 0; i < n; i++) {
            for(let j = i + 1; j < n; j++) {

                // 数值大的往后放
                if(nums[i] > nums[j]) {
                    temp = nums[i];
                    nums[i] = nums[j];
                    nums[j] = temp;
                }
            }
        }
    };

冒泡排序算法的时间复杂度为 O(n ^ 2),是比较稳定的一种算法,但是效率不是很高

使用 Array.sort

对于数组的排序,Array 的原型上也提供了一种方法 sort,该方法接受一个比较函数 fn,若 fn 的返回值小于0,则 a 会放在 b 之前,大于0则反之,等于0不做变换。同时 sort 也是对数组进行原地排序,不会进行复制。

    nums.sort((a, b) => a - b);

双指针+遍历

上面的冒泡和 sort 排序都是通用的算法,但是对于本题,数组中只存在 0,1,2 三种数字;我么只需要把 0 排到最前面,把 2 排到最后面,完成之后,原数组就是按照题目要求排序的了

具体实现:

  • 定义 left 为头部 0 的右边界,right 为尾部 2 的左边界,index 为当前遍历索引

  • 上述初始值为:left = index = 0, right = nums.length - 1;

  • index <= right 时,进入循环

    • 如果当前 nums[index] === 0, 将 nums[left] 和 nums[index] 交互,并将 left++, index++
    • 如果当前 nums[index] === 1, 将 index++, 无需进行交换操作
    • 如果当前 nums[index] === 2, 将 nums[right] 和 nums[index] 交互,并将 right--,这里不能将 index++,因为 nums[right] 也可能为 2
  • 结束循环后,nums 排序即完成

完整代码实现

    /**
     * @param {number[]} nums
     * @return {void} Do not return anything, modify nums in-place instead.
     */
    var sortColors = function (nums) {
        let left = 0, index = 0, right = nums.length - 1;

        // 定义交换方法,解构赋值
        const swap = (i, j) => {
            [nums[i], nums[j]] = [nums[j], nums[i]];
        }

        while(index <= right) {
            if(nums[index] === 0) {
                // 把 0 交换到前面
                swap(index, left);
                index++;
                left++;
            } else if(nums[index] == 1) {
                // 不用交换
                index++;
            } else {
                // 把 2 交换到后面
                // 因为可能交换过来的 nums[right] 也可能是 2,不能将index++
                swap(index, right);
                right--;
            }
        }
    };

上述代码的时间复杂度最大为 O(n),最多进行一次完成的循环,即可完成排序

小结

有一些思路,例如冒泡排序,是适用于较多数情况下都可进行的,但他的性能、效率一般都不是很好。针对于本道题,分析 nums 中只有三种元素,可以针对的进行一些操作,使得解法的效率变高。

# LeetCode 👉 HOT 100 👉 颜色分类 - 中等题

合集:LeetCode 👉 HOT 100,有空就会更新,大家多多支持,点个赞👍

如果大家有好的解法,或者发现本文理解不对的地方,欢迎留言评论 😄