|
| 1 | +### 题目描述 |
| 2 | + |
| 3 | +这是 LeetCode 上的 **[75. 颜色分类](https://leetcode.cn/problems/sort-colors/solution/by-ac_oier-7lwk/)** ,难度为 **中等**。 |
| 4 | + |
| 5 | +Tag : 「排序」、「双指针」 |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +给定一个包含红色、白色和蓝色、共 `n` 个元素的数组 `nums`,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 |
| 10 | + |
| 11 | +我们使用整数 `0`、 `1` 和 `2` 分别表示红色、白色和蓝色。 |
| 12 | + |
| 13 | +必须在不使用库的 `sort` 函数的情况下解决这个问题。 |
| 14 | + |
| 15 | +示例 1: |
| 16 | +``` |
| 17 | +输入:nums = [2,0,2,1,1,0] |
| 18 | + |
| 19 | +输出:[0,0,1,1,2,2] |
| 20 | +``` |
| 21 | +示例 2: |
| 22 | +``` |
| 23 | +输入:nums = [2,0,1] |
| 24 | + |
| 25 | +输出:[0,1,2] |
| 26 | +``` |
| 27 | + |
| 28 | +提示: |
| 29 | +* $n = nums.length$ |
| 30 | +* 1ドル <= n <= 300$ |
| 31 | +* `nums[i]` 为 `0`、`1` 或 `2` |
| 32 | + |
| 33 | + |
| 34 | +进阶: |
| 35 | +* 你可以不使用代码库中的排序函数来解决这道题吗? |
| 36 | +* 你能想出一个仅使用常数空间的一趟扫描算法吗? |
| 37 | + |
| 38 | +--- |
| 39 | + |
| 40 | +### 双指针 |
| 41 | + |
| 42 | +该题为经典的荷兰国旗问题,由于题目本质是要我们将数分成三段。 |
| 43 | + |
| 44 | +因此除了使用一个变量 `idx` 代指处理到哪一个 $nums[i]$ 以外,至少还需要两个变量来代指三段的边界: |
| 45 | + |
| 46 | +* 变量 `l` 为下一个填入 `0` 的位置(因此范围 $[0, l - 1]$ 均为 `0`,初始化 $l = 0,ドル代表空集) |
| 47 | +* 变量 `r` 为下一个填入 `2` 的位置(因此范围 $[r + 1, n - 1]$ 均为 `2`,初始化 $r = n - 1,ドル代表空集) |
| 48 | + |
| 49 | +由于 $[0, idx - 1]$ 均为处理过的数值(即 `0` 和 `2` 必然都被分到了两端),同时 $l - 1$ 又是 `0` 的右边界,因此 $[l, idx - 1]$ 为 `1` 的区间,而 $[idx, r]$ 为未处理的数值。 |
| 50 | + |
| 51 | +上述几个变量的定义是该题唯一需要理清的地方。 |
| 52 | + |
| 53 | +不失一般性,根据当前处理到的 $nums[idx]$ 进行分情况讨论: |
| 54 | + |
| 55 | +* $nums[idx] = 0$:此时将其与位置 `l` 进行互换(记住 `l` 为下一个待填入 `0` 的位置,同时 $[l, idx - 1]$ 为 `1` 的区间),本质是将 $nums[idx]$ 的 `0` 和 $nums[l]$ 的 `1` 进行互换,因此互换后将 `l` 和 `idx` 进行右移; |
| 56 | +* $nums[idx] = 1$:由于 $[l, idx - 1]$ 本身就是 `1` 的区间,直接将 `idx` 进行右移即可; |
| 57 | +* $nums[idx] = 2$:此时将其与位置 `r` 进行互换(`r` 为下一个待填入 `2` 的位置,但 $[idx, r]$ 为未处理区间),也就是我们互换后,只能明确换到位置 $nums[r]$ 的位置为 `2`,可以对 `r` 进行左移,但不确定新 $nums[idx]$ 为何值,因此保持 `idx` 不变再入循环判断。 |
| 58 | + |
| 59 | +最后当 $idx > r$(含义为未处理区间为空集),整个分三段过程结束。 |
| 60 | + |
| 61 | +Java 代码: |
| 62 | +```Java |
| 63 | +class Solution { |
| 64 | + public void sortColors(int[] nums) { |
| 65 | + int n = nums.length; |
| 66 | + int l = 0, r = n - 1, idx = 0; |
| 67 | + while (idx <= r) { |
| 68 | + if (nums[idx] == 0) swap(nums, l++, idx++); |
| 69 | + else if (nums[idx] == 1) idx++; |
| 70 | + else swap(nums, idx, r--); |
| 71 | + } |
| 72 | + } |
| 73 | + void swap(int[] nums, int i, int j) { |
| 74 | + int c = nums[i]; |
| 75 | + nums[i] = nums[j]; |
| 76 | + nums[j] = c; |
| 77 | + } |
| 78 | +} |
| 79 | +``` |
| 80 | +TypeScript 代码: |
| 81 | +```TypeScript |
| 82 | +function sortColors(nums: number[]): void { |
| 83 | + const n = nums.length |
| 84 | + let l = 0, r = n - 1, idx = 0 |
| 85 | + while (idx <= r) { |
| 86 | + if (nums[idx] == 0) swap(nums, l++, idx++) |
| 87 | + else if (nums[idx] == 1) idx++ |
| 88 | + else swap(nums, idx, r--) |
| 89 | + } |
| 90 | +} |
| 91 | +function swap(nums: number[], i: number, j: number): void { |
| 92 | + const c = nums[i] |
| 93 | + nums[i] = nums[j] |
| 94 | + nums[j] = c |
| 95 | +} |
| 96 | +``` |
| 97 | +Python 代码: |
| 98 | +```Python |
| 99 | +class Solution: |
| 100 | + def sortColors(self, nums: List[int]) -> None: |
| 101 | + n = len(nums) |
| 102 | + l, r, idx = 0, n - 1, 0 |
| 103 | + while idx <= r: |
| 104 | + if nums[idx] == 0: |
| 105 | + nums[l], nums[idx] = nums[idx], nums[l] |
| 106 | + l += 1 |
| 107 | + idx += 1 |
| 108 | + elif nums[idx] == 1: |
| 109 | + idx += 1 |
| 110 | + else: |
| 111 | + nums[r], nums[idx] = nums[idx], nums[r] |
| 112 | + r -= 1 |
| 113 | +``` |
| 114 | +* 时间复杂度:$O(n)$ |
| 115 | +* 空间复杂度:$O(1)$ |
| 116 | + |
| 117 | +--- |
| 118 | + |
| 119 | +### 最后 |
| 120 | + |
| 121 | +这是我们「刷穿 LeetCode」系列文章的第 `No.75` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。 |
| 122 | + |
| 123 | +在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 |
| 124 | + |
| 125 | +为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 |
| 126 | + |
| 127 | +在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 |
| 128 | + |
0 commit comments