题目叙述
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
进阶:
你可以不使用代码库中的排序函数来解决这道题吗?
你能想出一个仅使用常数空间的一趟扫描算法吗?
示例 1:
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
分析
本题主要要求实现一个时间复杂度为 O ( n ) O(n) O(n), 空间复杂度为 O ( 1 ) O(1) O(1)的算法。
方法1
统计每一个数字所出现的次数,并且存储起来,然后遍历这个次数数组,并按照数组的次数在原来的数组中进行存储数字。
class Solution {
public:
void sortColors(vector<int>& nums) {
int count[3] = {0};
// 本质是计数排序,时间复杂度为O(n)
for(int it: nums) count[it]++;
for(int i=0, index=0;i<3;i++)
for(int j=0;j<count[i];j++)
nums[index++]=i;
}
};
时间复杂度为 O ( k ∗ n ) O(k*n) O(k∗n), 空间复杂度为 O ( k ) O(k) O(k), 其中k为常数,所以时间复杂度为 O ( n ) O(n) O(n), 空间复杂度为 O ( 1 ) O(1) O(1)。
方法2
上面的解法其实是扫描原来的数组两遍,在相同的时空复杂度下,我们可以只扫描一遍即可解出本题。考虑使用双指针的方式,即由于颜色总共是有三种,那么定义两个指针,p指针指向当前已经排好序的红颜色的下一个位置,q指针指向当前已经排好序的蓝颜色的前一个位置,然后进行一次遍历进行归位,最终当红颜色和蓝颜色都排好序之后,白颜色也就自动排好序了。其基本的算法思想其实和调整数组顺序使奇数位于偶数前面思想一致。
class Solution {
public:
void sortColors(vector<int>& nums) {
int p = 0;//指向红色
int q = nums.size()-1; //指向蓝色
for(int i=0;i<q+1;){//注意这个地方一定要是q+1
if(nums[i]==0){
//和p的位置元素进行交换
int tmp = nums[i];
nums[i++] = nums[p];
nums[p++] = tmp;
}else if(nums[i]==2){
//和q的位置进行交换
int tmp = nums[i];
nums[i++] = nums[q];
nums[q--] = tmp;
}else i++;
}
}
};