题目描述:
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
注意:
不能使用代码库中的排序函数来解决这道题。
示例1:
输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]
原题链接:颜色分类
解题思路:
1、快速排序 :
可以利用快速排序中划分的思想,数组arr[L,R]划分后达到的效果为:arr[L,less] < target;arr[less + 1,more - 1] == target,arr[more,R] > target
。在这道题中target = 1。设当前元素为arr[cur],cur初始化为L,less初始化为L - 1,more初始化为R + 1,具体的划分过程为:
(1)当arr[cur] < target
时,交换arr[++less]和arr[cur++];
(2)当arr[cur] == target
时,cur++;
(3)当arr[cur] > target
时,交换arr[–more]和arr[cur]。
划分终止条件: 当cur == more
,划分完成。
注:(1)以上的操作没有考虑不存在target的情况,因此在这道题中target存在且为1。
时间复杂度和空间复杂度: 时间复杂度为O(N),空间复杂度为O(1)。
实现代码:
class Solution {
public void sortColors(int[] nums) {
if(nums == null || nums.length < 2)
return;
partition(nums, 0, nums.length - 1);
}
public int[] partition(int[] nums, int L, int R){
if(L < R){
int less = L - 1;//左边界
int more = R + 1;//右边界
int cur = L;
while(cur < more){
if(nums[cur] < 1)
swap(nums, ++less, cur++);
else if(nums[cur] == 1)
cur++;
else
swap(nums, --more, cur);
}
}
//返回arr[less + 1,more - 1]为target = 1的区间
return new int[]{less + 1, more - 1};
}
public void swap(int[] nums, int i, int j){
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
//这里顺便放上快速排序的写法:
//1、这种快排是基于随机快排和基于荷兰国旗问题的改进后的快排)
//2、基于荷兰国旗问题改进后的快排在会一次性将所有相等的数划分到相应的位置,这相对于经典快排中每次只划分一个数的做法速度会相对快一点
public void quickSort(int[] nums) {
if(nums == null || nums.length < 2)
return;
quickSort(nums, 0, nums.length - 1);
}
public void quickSort(int[] nums, int L, int R){
if(L < R){
swap(nums, R, (int)(L + Math.random() * (R - L + 1)));//随机选择一个数与数组最后的数交换
int[] p = partition1(nums, L, R);
quickSort(nums, L, p[0] - 1);
quickSort(nums, p[1] + 1, R);
}
}
public int[] partition1(int[] nums, int L, int R){
int less = L - 1;//左边界
int more = R;//右边界(先不对nums[R]操作)
int cur = L;
while(cur < more){
if(nums[cur] < nums[R])
swap(nums, ++less, cur++);
else if(nums[cur] == nums[R])
cur++;
else
swap(nums, --more, cur);
}
swap(nums, more, R);//将nums[R]和nums[more]交换,这样中间区间为nums[less + 1, more]
return new int[]{less + 1, more};
}
}