LeetCode 75 Sort Colors (Tag:Array Difficulty:Medium)

129 阅读2分钟

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

前言

关于 LeetCode 数组类型题目的相关解法,可见LeetCode 数组类型题目做前必看,分类别解法总结了题目,可以用来单项提高。觉得有帮助的话,记得多多点赞关注哦,感谢!

题目描述

给定一个包含红色、白色和蓝色,一共 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]

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

题解

这道题目是很有名的“荷兰国旗”问题,题目大意是0,1,2三个数组组成的数组,对其进行排序。 如果我们粗暴的用内置 sort 函数对其进行排序,那么时间复杂度为 O(nlogn), 那么对于只有三个数的排序来说,这种解法就显得大材小用。
其实看到题目的第一个想法是统计 0,1,2 每个数字出现的次数,然后重新放置回数组中,这种方法违背了原地排序的原则。
然后我们进一步想,我们遍历数组,用一个变量记录数字 0 部分最后一个下标,遍历过程中,遇到 0 就和前面的部分交换,变量加 1, 那么我们遍历一遍,数组前面一部分就已经是 0 了, 然后我们再从 0 之后的部分开始遍历,寻找 1 来交换,最后数组就是有序的了,时间复杂度为 O(n)。 代码如下:

/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var sortColors = function(nums) {
    let ptr = 0
    for (let i = 0; i < nums.length; i++) {
        if (nums[i] === 0) {
            swap(nums, i, ptr)
            ptr++
        }
    }
    
    for (let i = ptr; i < nums.length; i++) {
        if (nums[i] === 1) {
            swap(nums, i, ptr)
            ptr++
        }
    }
};

var swap = (nums, i, j) => {
    let tmp = nums[i]
    nums[i] = nums[j]
    nums[j] = tmp
}

上面的方法用到了两次遍历,那么可以不可以只用一次遍历来解决这个问题呢?我们可以想到双指针来解决单指针记录不足的问题,加上一个从右向左扩展的指针来记录数字 2,从左到右的扩展的指针记录数字 1,这样一次遍历就可以排好序了。代码如下:

/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
var sortColors = function(nums) {
    let left = cur = 0
    let right = nums.length - 1
    while (cur <= right) {
        if (nums[cur] === 0) {
            swap(nums, left, cur)
            left++
            cur++
        } else if (nums[cur] === 1) {
            cur ++
        } else {
            swap(nums, cur, right)
            right--
        }
    }
}

var swap = (nums, i, j) => {
    let tmp = nums[i]
    nums[i] = nums[j]
    nums[j] = tmp
}