先上题目:
给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。(我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。)
题目给出了例子,就是把形如{0,1,2,0,1}的数组排列成{0,0,1,1,2}。我一拍脑袋,这不简单,刷刷写完提交:
class Solution {
public:
void sortColors(vector<int>& nums) {
return sort(nums.begin(), nums.end());
}
};
提交果然成功过了,但我一想,事情应该没有这么简单,我仔细一看,之间下面写了一行字:
必须在不使用库的sort函数的情况下解决这个问题。
好吧好吧,重新开始。
题目要求原地对它们进行排序,因此不能建立新数组。通过交换来变出要的数组太难做到,那就需要知道这个数组中每一种元素的个数,然后对原来的数组进行赋值。 代码如下:
class Solution {
public:
void sortColors(vector<int>& nums) {
int array[3] = {0,0,0};
for(int i=0;i<nums.size();i++)
{
array[nums[i]]++;
}
int index = 0;
while(index<array[0])
{
nums[index] = 0;
index++;
}
while(index<array[0]+array[1])
{
nums[index] = 1;
index++;
}
while(index<array[0]+array[1]+array[2])
{
nums[index] = 2;
index++;
}
}
};
array[0], array[1], array[2]分别存储nums这个数组中0,1,2的个数,然后再按照顺序,对nums进行赋值。
代码是正确的,也能在规定时间内通过,但还是觉得太繁琐,看来一圈评论区,大佬是这么做的:
class Solution {
public:
void sortColors(vector<int>& nums) {
int num0 = 0, num1 = 0, num2 = 0;
for(int i = 0; i < nums.size(); i++) {
if(nums[i] == 0) {
nums[num2++] = 2;
nums[num1++] = 1;
nums[num0++] = 0;
}else if(nums[i] == 1) {
nums[num2++] = 2;
nums[num1++] = 1;
}else {
nums[num2++] = 2;
}
cout << num0 << " " << num1 << " " << num2 << endl;
}
}
};
由于我们最后得到的数组应该是由若干个0,若干个1,若干个2组成的数组,由于数组长度固定已知,其实我们只要知道从哪里开始0变成了1,又从哪里开始1变成了2。我之前的做法是先遍历一遍,知道0,1,2各自的数量,再遍历一遍根据得到的信息进行赋值,但是上述这段代码在一遍遍历中,完成了上述两件事情。
解释一下这段代码,以{0,2,1,0,1}为例:
我们用num0,num1,num2来记录最后一个0,1,2的位置,初始值均为0。
第一个数是0,那么比0大的1和2的位置都要往后顺移,由于这个位置是0,最后一个0也要往后移一个。[0]
第二个数是2,那么比2小的位置不变,最后一个2的位置要往后移一个。[0,2]
第三个数是1,那么比1大的2要往后瞬移一个,比1小的0不变,最后一个1的位置往后一个。[0,1,2]
第四个数是0,那么比0大的1和2的位置都要往后顺移,由于这个位置是0,最后一个0也要往后移一个。[0,0,1,2]
第五个数是1,那么比1大的2要往后瞬移一个,比1小的0不变,最后一个1的位置往后一个。[0,0,1,1,2]
num0 | num1 | num2 | nums | |
初始 | 0 | 0 | 0 | [0,2,1,0,1] |
① | 1 | 1 | 1 | [0,2,1,0,1] |
② | 1 | 1 | 2 | [0,2,1,0,1] |
③ | 1 | 2 | 3 | [0,1,2,0,1] |
④ | 2 | 3 | 4 | [0,0,1,2,1] |
⑤ | 2 | 4 | 5 | [0,0,1,1,2] |
由此,用了三个指针,完成了原地变换,大佬不愧是大佬。