[路飞] 颜色分类

195 阅读2分钟

记录 1 道算法题

颜色分类

leetcode-cn.com/problems/so…


要求:

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

0在最前面,1在中间,2在最后面。

需要进行排序,指针是比不可少的,简单的做法是把 0 全部换到左边,然后把 2 换到右边,那自然就完成排序。这是双指针的做法,一个在头一个在尾。但是如果是这样的话,每次移动尾指针要进行一个循环,找到尾部非 2 的下标。不然比如 [2,1,2],[0,2,2,2,0,2,1,1] 就不行。

单指针的做法则是进行两次遍历。先将 0 全部换到左边,然后把 2 换到右边。不过当我们把 0 归位的时候,其实我们的指针是指着 1 的开始位置,所以也可以将 1 换到这个位置。

首先提供一个交换位置的方法。

  1. 单指针
    function swap(arr, i, j) {
        const t = arr[i]
        arr[i] = arr[j]
        arr[j] = t
    }
    function sortColors(nums) {
        let j = 0
        for(let i = 0; i < nums.length; i++) {
            const num = nums[i]
            if (num === 0) {
                swap(nums, i, j)
                j++
            }
        }
        
        for(let i = j; i < nums.length; i++ {
            const num = nums[i]
            if (num === 1) {
                swap(nums, i, j)
                j++
            }
        }
        
        return nums
    }
  1. 双指针

先来个归类 0 和 2 的写法。问题是每次交换后都可能导致 nums[i] 会满足另一个条件。所以还得判断。

    function sortColors(nums) {
        let j = 0
        let k = nums.length - 1
        while (nums[k] === 2) {
            k--
        }
        for (let i = 0; i <= k; i++) {
            const num = nums[i]
            if (num === 2) {
                swap(nums, i, k)
                // 每次交换完之后 nums[i] 都可能是 0
                if (nums[i] === 0) {
                    swap(nums, i, j)
                    j++
                }
                k--
                // 尾部指针 k-- 很可能指着 2
                while (nums[k] === 2) {
                    k--
                }
            }

            if (num === 0) {
                swap(nums, i, j)
                // 同样每次交换完之后都可能是 2
                if (num[i] === 2) {
                    swap(nums, i, j)
                    k--
                    // 同样尾指针要跳过 2
                    while (nums[k] === 2) {
                        k--
                    }
                }
                j++
            }
        }

        return nums
    }

上面这一版可能不怎么精简,但执行效率还行,精简一点的写法就是尽可能减少交换之后 nums[i] 可能会出现满足另一个条件的概率。我们可以试一下归类 0 和 1。因为如果 0 指针移动了, 1 指针也要跟着移动,而每次移动后,左边都是已经确定的值,所以会减少 nums[i] 出现奇怪的数字。因为 0 和 1 是相邻的,所以不需要像 2 那样,中间一定要隔着东西。

    function sortColors(nums) {
        let j = 0
        let k = 0
        while(let i = 0; i < nums.length; i++) {
            const num = nums[i]
            if (num === 1) {
                swap(nums, i, k)
                k++
            } else if (num === 0) {
                swap(nums, i, j)
                // 当两个指针不是在一起时,说明已经确定第一个 1
                // 0 的下一个位置指针可能指向 1
                // 所以交换 0 的时候可能会把第一个 1 给换走,
                // 所以要换回来
                // 比如 [2,0,2,1,1,0]
                if (j < k) {
                    swap(nums, i, k)
                }
                j++
                k++
            }
        }
    }