「这是我参与2022首次更文挑战的第28天,活动详情查看:2022首次更文挑战」。
题目描述🌍
给定一个包含红色、白色和蓝色、共 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.length1 <= n <= 300nums[i]为0、1或2
进阶:
- 👑你可以不使用代码库中的排序函数来解决这道题吗?
- 你能想出一个仅使用常数空间的一趟扫描算法吗?
三路快排🍖
解题思路
题目本质就是让我们对只包含 0、1、2 三个数字的数组进行排序,且不能调代码库的排序函数!
显然我们可以手写各种排序方式,然后调用,但这并不是我们想要的,我们直接来完成进阶中的要求吧。
借用「三路快排」的思想,对数组进行一次遍历,如果是 0,则交换到左边;如果是 2,则交换到右边;一旦排完 0 和 2,那么 1 自然也就排好了。
⭐细节:
- 如果
2交换到右边,此时从右边交换过来的数可能是0、1或2,所以还需要再对当前位置继续判断; - 如果是
0交换到左边,从左边交换回来的数只能是0或1(因为是从左向右遍历的,一遇到2就交换到右边了),无需再判断当前位置的元素,(尽管交换回来的数是1)在随后的排序中会被自动排序好。
以下是排序过程中的示意图
其他解法:还有一种也是类似「三路快排」的解法,只不过 left 用于填充 0,而 right 用于填充 2,大同小异,细节上有所差异罢了,感兴趣的可以自己去写写看。
代码
Java
class Solution {
// QuickSort3Ways
public void sortColors(int[] nums) {
// 左指针: 填充0
int left = 0;
// 右指针: 填充2
int right = nums.length - 1;
// 遍历指针
int traverse = 0;
while (traverse <= right) {
if (nums[traverse] == 0) {
// 交换1, left右移, traverse右移
nums[traverse++] = nums[left];
nums[left++] = 0;
} else if (nums[traverse] == 2) {
// 交换2, right左移, traverse不动
int tmp = nums[right];
nums[right--] = nums[traverse];
nums[traverse] = tmp;
} else {
// 遇到1, 不做任何操作
traverse++;
}
}
}
}
C++
class Solution {
public:
void sortColors(vector<int> &nums) {
int left = 0;
int right = nums.size() - 1;
int i = 0;
while (i <= right) {
if (nums[i] == 0) {
nums[i++] = nums[left];
nums[left++] = 0;
} else if (nums[i] == 2) {
int temp = nums[right];
nums[right--] = nums[i];
nums[i] = temp;
} else {
i++;
}
}
}
};
时间复杂度:
空间复杂度:
知识点🌓
复习一下「快速排序」!
快排(一)
快速排序 (一) 的思想:
- 从数组中挑出一个基准元素
- ⭐在范围内,把 (从左到右) 第一个比基准值大的元素与 (从右到左) 第一个比基准值小的元素交换,重复进行;排完后将基准元素与数组中间位置的元素交换,实现基准元素
base左边都小与base,右边都大于base - 递归地把左右分区再进行如上排序
// 快排(一)
public static void quickSort(int[] data) {
subSort(data, 0, data.length - 1);
}
private static void subSort(int[] data, int start, int end) {
if (start < end) {
int base = data[start];
int low = start;
int high = end + 1;
while (true) {
while (low < end && data[++low] - base <= 0)
;
while (high > start && data[--high] - base >= 0)
;
if (low < high) {
swap(data, low, high);
} else {
break;
}
}
swap(data, start, high);
subSort(data, start, high - 1);
subSort(data, high + 1, end);
}
}
private static void swap(int[] data, int i, int j) {
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
快排(二)
快速排序 (二) 的思想:
- 从数组中挑出一个基准元素
- ⭐在指定范围内,维护一个有效数组存放比基准元素小的元素,遍历完指定范围数组后,再将基准元素替换到中间位置(此时右边也都比基准元素大)
- 递归地把左右分区再进行如上排序
// 快排(二)
public void quickSort2(int[] nums, int leftBound, int rightBound) {
if (leftBound < rightBound) {
int partitionIndex = partition(nums, leftBound, rightBound);
quickSort2(nums, leftBound, partitionIndex - 1);
quickSort2(nums, partitionIndex + 1, rightBound);
}
}
// 排序后返回基准值
private int partition(int[] nums, int left, int right) {
int pivot = left;
int index = pivot + 1;
for (int i = index; i <= right; i++) {
if (nums[i] < nums[pivot]) {
swap(nums, i, index);
// 有效数组的末尾指针后移
index++;
}
}
swap(nums, pivot, index - 1);
// 返回基准值
return index - 1;
}
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
最后🌅
该篇文章为 「LeetCode」 系列的 No.26 篇,在这个系列文章中:
- 尽量给出多种解题思路
- 提供题解的多语言代码实现
- 记录该题涉及的知识点
👨💻争取做到 LeetCode 每日 1 题,所有的题解/代码均收录于 「LeetCode」 仓库,欢迎随时加入我们的刷题小分队!