题目描述
给定一个包含红色、白色和蓝色、共 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、2的数量,再重新赋值
java代码:
class Solution {
public void sortColors(int[] nums) {
// 统计数量
int num0 = 0, num1 = 0, num2 = 0;
for (int num : nums) {
if (num == 0) {
num0 += 1;
}
if (num == 1) {
num1 += 1;
}
if (num == 2) {
num2 += 1;
}
}
// 原地修改值
for (int i = 0; i < nums.length; i++) {
if (num0 > 0) {
nums[i] = 0;
num0 --;
continue;
}
if (num1 > 0) {
nums[i] = 1;
num1 --;
continue;
}
if (num2 > 0) {
nums[i] = 2;
num2 --;
}
}
}
}
复杂度
- 时间复杂度:
O(n)
- 空间复杂度:
O(1)
解法2:单指针
对数组进行两次遍历。
在第一次遍历中,我们将数组中所有的 0 交换到数组的头部。
在第二次遍历中,我们将数组中所有的 1 交换到头部的 0 之后。
此时,所有的 2 都出现在数组的尾部,这样我们就完成了排序
java代码
class Solution {
public void sortColors(int[] nums) {
// 第1次遍历,将数组中所有的 0 交换到数组的头部
int pos = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == 0) {
int temp = nums[pos];
nums[pos] = nums[i];
nums[i] = temp;
pos ++;
}
}
// 第2次遍历,将数组中所有的 1 交换到头部的 0 之后
for (int i = pos; i < nums.length; i++) {
if (nums[i] == 1) {
int temp = nums[pos];
nums[pos] = nums[i];
nums[i] = temp;
pos ++;
}
}
}
}
复杂度
- 时间复杂度:
O(n)
- 空间复杂度:
O(1)
解法3:双指针
可以只遍历一遍。
用指针 pos0 来表示0的下个可交换位置,pos1 来表示1的下个可交换位置,初始值都为 0。当我们从左向右遍历整个数组时:
- 如果找到了 1,那么将其与 nums[pos1] 进行交换,并将 pos1 向后移动一个位置;
- 如果找到了 0, 那么将其与 nums[pos0] 进行交换后,还需要注意,连续的 0 之后是连续的 1,那么我们可能会把一个 1 交换出去了,还需要换回来;
java代码:
class Solution {
public void sortColors(int[] nums) {
int pos0 = 0, pos1 = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == 0) {
int temp = nums[pos0];
nums[pos0] = nums[i];
nums[i] = temp;
// 这样可能把0后面的1交换到i位置了,需要把这个1再交换到pos1的位置
if (pos0 < pos1) {
temp = nums[pos1];
nums[pos1] = nums[i];
nums[i] = temp;
}
pos0 ++;
pos1 ++;
} else if (nums[i] == 1) {
int temp = nums[pos1];
nums[pos1] = nums[i];
nums[i] = temp;
pos1 ++;
}
}
}
}
复杂度
- 时间复杂度:
O(n)
- 空间复杂度:
O(1)