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

426 阅读2分钟

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

此题是双指针类型的第10题。友情提示,此题仍然为双指针类型。

一直有刷题习惯,最近才看到掘金举办了刷题活动,特来参加!此题为第10题。不得不说掘金的主题就是漂亮呀!赞。

本文正在参与掘金团队号上线活动,点击 查看大厂春招职位

一、题目描述:

颜色分类

给定一个包含红,白,蓝且长度为 n 的数组,将数组元素进行分类使相同颜色的元素相邻,并按照红、白、蓝的顺序进行排序。

我们可以使用整数 0,1 和 2 分别代表红,白,蓝。

样例 1

输入 : [1, 0, 1, 2]
输出 : [0, 1, 1, 2]
解释 : 原地排序。

?>挑战
一个相当直接的解决方案是使用计数排序扫描2遍的算法。
首先,迭代数组计算 0,1,2 出现的次数,然后依次用 0,1,2 出现的次数去覆盖数组。
你否能想出一个仅使用常数级额外空间复杂度且只扫描遍历一遍数组的算法?

二、思路分析:

方法描述时间复杂度空间复杂度
双指针法双指针的变种,三指针法O(N)O(N)(1)(1)
第一步nums[mid]==2时,rightmid交换位置,right--O(N)O(N)(1)(1)
第二步nums[mid]==0时,leftmid交换位置,left++,mid++O(N)O(N)(1)(1)
第三步nums[mid]==1的,mid++O(N)O(N)(1)(1)

三、AC 代码:

/*
 * @lc app=leetcode.cn id=75 lang=java
 *
 * [75] 颜色分类
 */

// @lc code=start
class Solution {
    /**
     * three tips: 
     * nums.length can be 2
     * mid can equal right
     * mid pcocess cases
     * 
     * three pointer
     * origin array:
     * 1 0 2 1 0 1 0
     * result array:
     * 0 0  1 1 1   2 2 2
     *   |        |
     *   L    MID R
     * @param nums
     */
    public void sortColors(int[] nums) {
        // back onditions
        if (nums == null ) {
            // fix bug ,length can be 2 or 1 
            return;
        }

        int left = 0;
        int mid = 0;
        int right = nums.length-1;
        // fixt bug ,mid can equal right
        while(mid <= right){
            if(nums[mid] == 0){
                // dose not care about right pointer
                // keep pointer left to the left of 0
                // 0  | 
                //    L
                swap(nums,mid,left);
                left++;
                mid++;
            }else if(nums[mid] == 2){
                // dose not care about mid pointer and right pointer
                // keep pointer right with 2 to the right
                //  | 2
                //  R
                swap(nums, mid, right);
                right--;
                // fix bug ,mid dose not process
            }else{
                // dose not care about left pointer and right pointer
                mid++;
            }
        }
    }

    private void swap(int[] nums, int start, int end) {
        int temp =nums[end];
        nums[end] = nums[start];
        nums[start] = temp; 
    }
}
// @lc code=end

四、总结:

此题的难点在于第三个指针:

  1. 第一次做题,很难想到再加一根指针mid
  2. 处理指针mid的思路不清晰,不清楚如何移动,mid左移或右移的依据

题目收获:

  1. 此题为双指针的变形+相向双指针
  2. 引入mid指针之后,为后面划分算法、二分法可以做良好的铺垫

思路收获:

  1. 我们建立首尾双指针leftright,分别指示0/1边界和1/2边界。left左边(不含left)全为0,right右边(不含right)全为2。
  2. 初始化leftmid为0,right为len(nums)-1
  3. 第三个指针mid从left起向right移动,边扫描边实时更新两个边界。
    • nums[mid]为 0 :交换第 mid个和第left个元素,并将 left 指针和mid指针都向右移。
    • nums[mid]为 2 :交换第 mid个和第 right个元素,并将 right指针左移
    • nums[mid]为 1 :将指针mid右移。
      补充一下,当midleft交换后,mid能够后移,因为此时nums[mid]可能为0,后面还需要与left交换。