这道题目要求我们对一个仅包含0、1、2三种元素的数组进行原地排序,使得所有0排在前面,1居中,2在最后。题目特别强调不能使用内置的sort函数,这实际上是在考察我们对特定场景下排序算法的理解和应用能力。
这道题的特殊之处在于:
在常规排序算法中,快速排序和归并排序的时间复杂度都是O(nlogn),而这道题由于元素种类有限,理论上可以做到O(n)的时间复杂度。
最直观的解法是计数排序:
这种方法虽然简单,但需要两次遍历,且严格来说不算"原地"排序(因为需要额外空间存储计数)。不过在实际编码中,我们可以优化为只使用常数空间来存储计数。
题目提示可以使用双指针,这实际上是荷兰国旗问题的经典解法。让我们深入分析这种解法的原理和实现细节。
我们维护三个指针:
初始时,red和white都指向数组开头,blue指向数组末尾。我们通过一个指针i遍历数组,根据当前元素的值决定如何交换元素。
这个算法的精妙之处在于:
通过不断交换,我们可以确保每个元素最终都在正确的位置。
cpp复制class Solution {
public:
void sortColors(vector<int>& nums) {
int red = 0, white = 0, blue = nums.size() - 1;
while (white <= blue) {
if (nums[white] == 0) {
swap(nums[red], nums[white]);
red++;
white++;
} else if (nums[white] == 1) {
white++;
} else {
swap(nums[white], nums[blue]);
blue--;
}
}
}
};
在实际编码中,有几个边界情况需要注意:
这个条件确保了当white超过blue时,所有元素都已经被处理。如果写成white < blue,可能会漏掉最后一个元素的处理。
在交换0和2时顺序很重要:
在实现这类双指针算法时,可以:
例如对于输入[2,0,2,1,1,0],可以这样跟踪:
code复制初始:[2,0,2,1,1,0] red=0,white=0,blue=5
交换2和0:[0,0,2,1,1,2] red=0,white=1,blue=4
white=1是0,交换:[0,0,2,1,1,2] red=1,white=2
white=2是2,交换:[0,0,1,1,2,2] red=1,white=2,blue=3
white=2是1,跳过:[0,0,1,1,2,2] white=3
white=3是1,跳过:[0,0,1,1,2,2] white=4
结束
如果题目扩展到4种或更多颜色,这个算法依然适用,只需要增加更多的指针。例如对于4种颜色,可以维护3个分割指针。
这种算法在以下场景有实际应用:
一些类似的变种问题:
在实际编码中,我发现以下几点特别重要:
一个常见的错误是在处理2时也移动white指针,这会导致某些0或2被跳过。正确的做法是只移动blue指针,因为交换过来的元素可能还需要处理。