LeetCode第75题“颜色分类”要求对包含红色、白色和蓝色的数组进行排序,红色、白色和蓝色分别用0、1和2表示。这个问题可以通过荷兰国旗问题的解决方案来实现,该算法的时间复杂度为O(N),空间复杂度为O(1)。
方法一
代码实现
我们可以使用双指针(two-pointer)的方法来解决这个问题:
#include <vector>
#include <iostream>
using namespace std;
class Solution {
public:
void sortColors(vector<int>& nums) {
int left = 0, right = nums.size() - 1, current = 0;
while (current <= right) {
if (nums[current] == 0) {
swap(nums[current], nums[left]);
left++;
current++;
} else if (nums[current] == 1) {
current++;
} else if (nums[current] == 2) {
swap(nums[current], nums[right]);
right--;
}
}
}
};
int main() {
Solution solution;
vector<int> nums = {2, 0, 2, 1, 1, 0};
solution.sortColors(nums);
for (int num : nums) {
cout << num << " ";
}
cout << endl;
return 0;
}
解释
-
初始化三个指针:
left
指向最左边的0的下一个位置。right
指向最右边的2的前一个位置。current
用于遍历数组。
-
遍历数组并进行交换:
- 如果
nums[current]
为0,则与nums[left]
交换,同时将left
和current
指针都向右移动一位。 - 如果
nums[current]
为1,则仅将current
向右移动一位。 - 如果
nums[current]
为2,则与nums[right]
交换,同时将right
指针向左移动一位,但current
不移动,因为交换过来的值还需要处理。
- 如果
关键点
- 通过交换
nums[current]
和nums[left]
,我们确保所有0都在数组的左边。 - 通过交换
nums[current]
和nums[right]
,我们确保所有2都在数组的右边。 - 所有1都将自动排在中间。
时间复杂度和空间复杂度
- 时间复杂度:O(N),因为我们只需要遍历数组一次。
- 空间复杂度:O(1),因为我们只使用了常数级别的额外空间。
这种方法高效且简单,适用于大多数情况下需要对包含少数类别的数组进行分类排序的场景。
方法二
有一个简单的思路,这种方法虽然简单直观,但它不是题目要求的单次遍历解决方案。不过,对于理解问题和学习排序的基本概念非常有帮助。可以分为两个步骤:
- 第一次遍历:统计数组中0、1、2的出现次数。
- 第二次遍历:根据统计结果重新填充数组。
代码实现
#include <vector>
#include <iostream>
using namespace std;
class Solution {
public:
void sortColors(vector<int>& nums) {
// Step 1: Count the number of 0s, 1s, and 2s
int count0 = 0, count1 = 0, count2 = 0;
for (int num : nums) {
if (num == 0) {
count0++;
} else if (num == 1) {
count1++;
} else if (num == 2) {
count2++;
}
}
// Step 2: Overwrite the array with the sorted order
int index = 0;
for (int i = 0; i < count0; i++) {
nums[index++] = 0;
}
for (int i = 0; i < count1; i++) {
nums[index++] = 1;
}
for (int i = 0; i < count2; i++) {
nums[index++] = 2;
}
}
};
int main() {
Solution solution;
vector<int> nums = {2, 0, 2, 1, 1, 0};
solution.sortColors(nums);
for (int num : nums) {
cout << num << " ";
}
cout << endl;
return 0;
}
解释
-
统计0、1、2的出现次数:
int count0 = 0, count1 = 0, count2 = 0; for (int num : nums) { if (num == 0) { count0++; } else if (num == 1) { count1++; } else if (num == 2) { count2++; } }
- 遍历数组,使用三个变量
count0
、count1
和count2
分别统计0、1、2的出现次数。
- 遍历数组,使用三个变量
-
重新填充数组:
int index = 0; for (int i = 0; i < count0; i++) { nums[index++] = 0; } for (int i = 0; i < count1; i++) { nums[index++] = 1; } for (int i = 0; i < count2; i++) { nums[index++] = 2; }
- 使用
index
变量从头开始填充数组。 - 先填充
count0
个0,再填充count1
个1,最后填充count2
个2。
- 使用
时间复杂度和空间复杂度
- 时间复杂度:O(N),因为我们遍历了数组两次。
- 空间复杂度:O(1),因为我们只使用了常数级别的额外空间。
方法三(单指针)
#include <vector>
using std::vector;
/*
* @lc app=leetcode.cn id=75 lang=cpp
*
* [75] 颜色分类
*/
// @lc code=start
class Solution {
public:
void sortColors(vector<int>& nums) {
int index = 0;
for(int i = 0;i < nums.size() ;i++){
if(nums[i] == 0){
swap(nums,i,index);
index ++;
}
}
for(int i = 0; i < nums.size();i++){
if(nums[i] == 1){
swap(nums,i,index);
index ++;
}
}
}
void swap(vector<int>& nums,int index1,int index2){
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
};
// @lc code=end
方法四(双指针)
#include <vector>
using std::vector;
/*
* @lc app=leetcode.cn id=75 lang=cpp
*
* [75] 颜色分类
*/
// @lc code=start
class Solution {
public:
void sortColors(vector<int>& nums) {
int left = 0;
int right = nums.size() - 1;
for(int i = 0;i <= right;i++){
if(nums[i] == 0){
swap(nums,i,left);
left ++;
}else if(nums[i] == 2){
swap(nums,i,right);
right --;
if(nums[i] != 1){
i --;
}
}
}
}
void swap(vector<int>& nums,int index1,int index2){
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
};
// @lc code=end