先上题目:
给定一个包含红色、白色和蓝色、共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]
由此,用了三个指针,完成了原地变换,大佬不愧是大佬。