一、题目描述
给定一个包含红色、白色和蓝色、共 n
个元素的数组 nums
,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
我们使用整数 0
、 1
和 2
分别表示红色、白色和蓝色。
必须在不使用库内置的 sort 函数的情况下解决这个问题。
示例 1:
输入:nums = [2,0,2,1,1,0] 输出:[0,0,1,1,2,2]
示例 2:
输入:nums = [2,0,1] 输出:[0,1,2]
提示:
n == nums.length
1 <= n <= 300
nums[i]
为0
、1
或2
二、解题思路
这个问题通常被称为“荷兰国旗问题”,可以使用“三指针”方法来解决。基本思路是将数组分为四个部分:
- 小于1的部分(即0的部分)
- 等于1的部分
- 大于1的部分(即2的部分)
- 未排序的部分
我们使用三个指针:
p0
指向小于1部分的最后一个元素的下一位。curr
指向当前考虑的元素。p2
指向大于1部分的第一个元素。
算法步骤如下:
1. 初始化 p0 = 0
和 p2 = nums.length - 1
。
2. 当 curr <= p2
:
- 如果
nums[curr] == 0
,交换nums[curr]
和nums[p0]
,并将p0
和curr
都增加1。 - 如果
nums[curr] == 1
,curr
增加1。 - 如果
nums[curr] == 2
,交换nums[curr]
和nums[p2]
,并将p2
减少1。注意这里curr
不增加,因为交换过来的元素还没有被检查。
重复上述步骤,直到 curr
超过 p2
,此时数组就被原位排序了。
三、具体代码
class Solution {
public void sortColors(int[] nums) {
int p0 = 0, curr = 0, p2 = nums.length - 1;
int temp;
while (curr <= p2) {
switch (nums[curr]) {
case 0: {
// 交换nums[curr]和nums[p0],并将p0右移
temp = nums[curr];
nums[curr] = nums[p0];
nums[p0] = temp;
p0++;
curr++;
break;
}
case 1:
// 如果是1,直接移动当前指针
curr++;
break;
case 2: {
// 交换nums[curr]和nums[p2],并将p2左移
temp = nums[curr];
nums[curr] = nums[p2];
nums[p2] = temp;
p2--;
break;
}
}
}
}
}
四、时间复杂度和空间复杂度
1. 时间复杂度
- 该算法只涉及一次遍历数组,每个元素只被查看和交换一次。
- 因此,时间复杂度是线性的,即 O(n),其中 n 是数组的长度。
2. 空间复杂度
- 该算法是原地排序算法,除了几个用于索引和临时存储的变量外,没有使用额外的存储空间。
- 因此,空间复杂度是常数的,即 O(1)。
五、总结知识点
-
三指针技术:这是一种用于解决特定排序问题的算法技巧,特别是在数组中存在多个不同的元素时。在这个问题中,三个指针
p0
、curr
和p2
分别用来维护0、1和2的边界。 -
原地排序:算法直接在输入数组上进行操作,不使用额外的存储空间来存储数组元素,从而实现空间复杂度为 O(1)。
-
交换操作:在算法中,当需要将元素放到正确的位置时,使用了交换操作。这是排序算法中常见的操作,用来改变元素的位置。
-
switch 语句:在 Java 中,switch 语句是一种多分支选择结构,用于根据变量的值来执行不同的代码块。在这个算法中,它用来根据当前元素的值(0、1 或 2)来执行相应的操作。
-
循环结构:
while
循环用于遍历数组,直到curr
指针超过p2
指针,表示数组已经被完全排序。 -
变量赋值和操作:代码中使用了基本的变量赋值和操作,如自增(
p0++
、curr++
、p2--
)和交换(temp = nums[curr]; nums[curr] = nums[p0]; nums[p0] = temp;
)。 -
算法设计原则:这个实现展示了算法设计中的一个重要原则,即通过减少不必要的操作来优化性能。例如,当
nums[curr]
为1时,不需要进行交换,直接移动curr
指针即可。
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。