问题描述:
现有红白蓝三个不同颜色的小球,乱序排列在一起,请重新排列这些小球,使得红白蓝三色的同颜色的球在一起。这个问题之所以叫荷兰国旗问题,是因为我们可以将红白蓝三色小球想象成条状物,有序排列后正好组成荷兰国旗。
分析
这个问题我们可以将这个问题视为一个数组排序问题。红白蓝分别对应数字0、1、2。红、白、蓝三色小球数量并不一定相同。
1 | 0 | 2 | 0 | 1 | 1 | 2 | 0 | 2 | 1 |
0 | 0 | 0 | 1 | 1 | 1 | 1 | 2 | 2 | 2 |
数据如第一行所示,排序之后结果为第二行。 这个问题就是一个排序问题。需要把小于1的放在前面,1放在中间,2放在最后。
我们接下来看看如何解决这个问题。
解法一:
1.遍历数组,统计红白蓝三色球(0,1,2)的个数
2.根据红白蓝三色球(0,1,2)的个数重排数组
时间复杂度为O(n)
代码如下:
class Solution {
public void sortColors(int arr[]) {
if (arr.length <= 1) {
return;
}
// 统计个数
int red = 0, white = 0, blue = 0;
for (int i = 0; i < arr.length; ++i) {
if (arr[i] == 0) {
++red;
} else if (arr[i] == 1) {
++white;
} else {
++blue;
}
}
// 重新布局
for (int i = 0; i < arr.length; ++i) {
if (red > 0) {
arr[i] = 0;
--red;
} // if
else if (white > 0) {
arr[i] = 1;
--white;
} // else
else {
arr[i] = 2;
}
}
}
}
解法二:
我们可以把数组分成三部分,前部(全部是0),中部(全部是1)和后部(全部是2)三个部分,每一个元素(红白蓝分别对应0、1、2)必属于其中之一。
将前部和后部各排在数组的前边和后边,中部自然就排好了。
设置两个指针begin指向前部的末尾的下一个元素(刚开始默认前部无0,所以指向第一个位置),end指向后部开头的前一个位置(刚开始默认后部无2,所以指向最后一个位置),然后设置一个遍历指针i,从头开始进行遍历。
(1)若遍历到的位置为1,则说明它一定属于中部,根据总思路,中部的我们都不动,然后i向前移动一个位置。
(2)若遍历到的位置为0,则说明它一定属于前部,于是就和begin位置进行交换,然后i向前移动一个位置,begin也向前移动一个位置(表示前边的已经都排好了)。
(3)若遍历到的位置为2,则说明它一定属于后部,于是就和end位置进行交换,由于交换完毕后i指向的可能是属于前部的,若此时i前进则会导致该位置不能被交换到前部,所以此时i不前进。而同1),end向前移动一个位置。
代码如下:
class Solution {
public void sortColors(int arr[]) {
int begin = 0, end = arr.length - 1, i = 0, temp;
while (i <= end) {
if (arr[i] == 0) {
temp = arr[begin];
arr[begin] = arr[i];
arr[i] = temp;
++begin;
++i;
} else if (arr[i] == 1) {
++i;
} else {
temp = arr[i];
arr[i] = arr[end];
arr[end] = temp;
--end;
}
}
}
}