1. 题目描述
题目描述:
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
进阶要求:
1. 你可以不使用代码库中的排序函数来解决这道题吗?
2. 你能想出一个仅使用常数空间的一趟扫描算法吗?
示例:
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
输入:nums = [2,0,1]
输出:[0,1,2]
输入:nums = [0]
输出:[0]
输入:nums = [1]
输出:[1]
2. 解题思路
方法一:计数排序法(简单但不符合题意要求)
思路
- 主旨:使用三个变量,遍历一遍数组,记录每种颜色出现的数量。然后再遍历一遍数组,根据数量分别将数组置为相应颜色的值。
- 操作流程:
- 定义三个变量
red,white,分别记录红色、白色出现的数量(蓝色无需记录,除了红色和白色之外,都是蓝色)。 - 遍历数组,如果值为
0,red的值加一;如果值为1,white的值加一。 - 再次遍历数组,为数组的
0至red位置的元素,赋值为0;为数组的red至red+white位置的元素,赋值为1;为数组的red+white至末尾nums.length-1位置的元素,赋值为2;
- 定义三个变量
代码实现
public void sortColors(int[] nums) {
if (nums.length < 2) {
return;
}
// 首次遍历数组,分别记录红色、白色出现的次数
int red = 0, white = 0;
for(int i = 0; i < nums.length; i++) {
if (nums[i] == 0) {
red++;
} else if (nums[i] == 1) {
white++;
}
}
// 再次遍历数组,为相应的颜色赋值
int index = 0;
while (index < red) {
nums[index++] = 0;
}
while (index < (red + white)) {
nums[index++] = 1;
}
while (index < (nums.length)) {
nums[index++] = 2;
}
}
复杂度分析
- 时间复杂度:O(n)。总共有n个元素,第一次遍历计数需要n步,第二次遍历赋值需要n步,总共2n次操作。
- 空间复杂度:O(1)。只需要常数的空间存放若干变量。
执行结果
方法二:双指针法(荷兰国旗问题,推荐)
背景
这是一道经典的荷兰国旗问题,是荷兰计算机科学家Dijkstra提出的。我们在大学《数据结构与算法》这门课程里,学过一个叫做Dijkstra最短路径算法的算法,就是这个科学家提出的。《高频面试考题:荷兰旗问题》这篇文章详细介绍了荷兰国旗问题,感兴趣的可以参考下。本文就不做展开了。
思路
- 主旨:在数组中定义左中右三块区域,分别赋值0,1,2,构成红、白、蓝三块区域。然后遍历整个数组,比较当前元素是否为0,1或者2。再分别与红区的右端或是蓝区的左端元素交换。
- 操作流程:
- 定义p0、i、p2三个指针,划定红白蓝三块区域。初始值p0、i指向数组最左端,p2指向数组最右端。
这三快区域分别为:
- 红区:[p0, i]。(左右闭合)
- 白区:(i, p2)(左右打开)
- 蓝区:[p2, nums.length](左右闭合)
- 遍历数组,直到i>p2。
- 若i指向元素值为0(该值应放置于红区[p0, i]),则交换i与p0,并使p0和i向右移动一格。
- 若i指向的元素为1(该值应放置于红区(i, p2)),则不做交换,只使i向右移动一个。
- 若i指向的元素为2(该值应放置于蓝区[p2, nums.length]),则交换i与p2,但只使p2向左移动一格。之所以不使i加一,是因为从p2交换到i的元素,有可能也是值为2的。如果跳过此值,则(i,p2)中就出现了值为2的元素,也就是白区出现了蓝色,是不符合定义的。
- 定义p0、i、p2三个指针,划定红白蓝三块区域。初始值p0、i指向数组最左端,p2指向数组最右端。
这三快区域分别为:
代码实现
public void sortColors(int[] nums) {
int p0 = 0, i = 0, p2 = nums.length -1;
while (i <= p2) {
if (nums[i] == 0) {
swap(nums, i, p0);
p0++;
i++;
} else if (nums[i] == 2) {
swap(nums, i, p2);
p2--;
} else {
i++;
}
}
}
public static void swap(int[] nums, int a, int b) {
int temp = nums[a];
nums[a] = nums[b];
nums[b] = temp;
}
复杂度分析
- 时间复杂度:O(n)。总共有n个元素,只需遍历一遍,所以会遍历n个元素。
- 空间复杂度:O(1)。只需要常数的空间存放若干变量。
执行结果
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情