题目
- 给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
- 此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
注意
- 不能使用代码库中的排序函数来解决这道题
示例
输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]
进阶
- 一个直观的解决方案是使用计数排序的两趟扫描算法。
首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。 - 你能想出一个仅使用常数空间的一趟扫描算法吗?
解法:
方法一 (不推荐)
- 我们直接用map进行存储第一遍遍历的结果,key是数组中元素出现的个数,key是数组中的元素(0,1,2),得到map之后,利用map输出结果
- java代码实现
public void sortColors(int[] nums) {
if (nums == null || nums.length == 1) {
return;
}
Map<Integer, Integer> result = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
if (!result.containsKey(nums[i])) {
result.put(nums[i], 0);
}
Integer integer = result.get(nums[i]);
result.put(nums[i], integer + 1);
}
int index = 0;
if (result.containsKey(0)) {
for (int i = 0; i < result.get(0); i++) {
nums[index ++] = 0;
}
}
if (result.containsKey(1)) {
for (int i = 0; i < result.get(1); i++) {
nums[index ++] = 1;
}
}
if (result.containsKey(2)) {
for (int i = 0; i < result.get(2); i++) {
nums[index ++] = 2;
}
}
}
方法二
- 本题我们使用三个指针p0,p1,curr分别来追踪0的最右边界,2的最左边界,和当前考虑的元素
- 本体的思路是沿着数组curr指针进行移动,若nums[curr] = 0,则将其与nums[p0]互换,若nums[curr] = 2,则将其与nums[p2]互换
- 算法:
(1)初始化0的右边界,p0 = 0,在整个算法中nums[ids < p0] = 0;
(2)初始化2的左边界,p1 = n - 1。在整个算法的执行过程中nums[ids > p1] = 2
(3)初始化当前考虑的元素序号,curr = 0
(4)while curr <= p1
(4.1)若nums[curr] = 0:交换第curr个和第p0个元素,并将指针都向右移
(4.2)若nums[curr] = 2:交换第curr个和第p1个元素,并将p2指针左移
(4.3)若nums[curr] = 1:将指针curr右移 - java实现,可以参考代码注释
/**
* 三指针移动
* p0 p1 curr
* p0表示的是当前数组的第一个元素下标
* p1表示的是当前数组的最后一个元素的下标
* curr表示的是当前需要判断的数组元素的下标
* (1)如果当前curr的下标表示1 curr右移
* (2)如果当前curr的下标表示0 nums[curr]与nums[p0]交换,同时移动p0和curr,因为我们不能让curr的下标小于p0
* 并且p0和curr都是在同一个初始位置上指着,所以p0的左边肯定是0,这时候就没有必要curr保持不动,curr与p0同时移动即可
* (3)如果当前curr的下标表示2 nums[curr]与nums[p1]交换,p1右移,curr不变这时候是为了判断交换后的新元素是0还是1还是2
* @param nums
*/
public void sortColors(int[] nums) {
// 放在数组的最前面
int p0 = 0;
// 放在数组的最后的位置
int p1 = nums.length - 1;
// 表示的是当前的判断的下标
int curr = 0;
while (curr <= p1){
if (nums[curr] == 0) {
// 交换nums[curr] 与 nums[p0]的值
// curr与p0的坐标都右移
int tmp = nums[p0];
nums[p0] = nums[curr];
nums[curr] = tmp;
p0 ++;
curr ++;
} else if (nums[curr] == 2) {
// 交换nums[curr] 与 nums[p1]的值
// p1的值左移
int tmp = nums[p1];
nums[p1] = nums[curr];
nums[curr] = tmp;
p1 --;
} else {
// 若当前元素为1移动curr的下标
curr ++;
}
}
}