引子

我在北京的一个现任职京东零售的一个朋友,最近投了阿里巴巴的简历,面试官一面就考察了一道算法题,我与他交流了一下,发现一道荷兰国旗的问题。

假如有一个数组,里面的元素由红、白、蓝三个颜色组成,请你写出一个算法,使数组的元素按照红、白、蓝顺序分布。

荷兰国旗问题_指针

至于为什么叫荷兰国旗,你对比一下,荷兰国旗的三色分布就知道了。

荷兰国旗问题_指针_02

遗憾的是,我这个朋友没有答出来,于是和阿里失之交臂。

思路

见到这种题,首先第一反应就是是不是能在一次遍历搞定,就是时间复杂度在O(n)解决。

先把这个问题简单化一点,如果这个数组只有两个颜色,红色和蓝色,那么你会不会。

我们可以定义左右两个指针,分别放到数组的两边。

荷兰国旗问题_算法_03

然后,左指针向右移动,直到遇到蓝色的元素,右指针向左移动,直到遇到红色的元素。

荷兰国旗问题_算法_04

然后交换两个元素。

荷兰国旗问题_指针_05

左右指针继续移动,直到左指针的索引小于右指针,完成。

荷兰国旗问题_数组_06

如果这个问题清楚了,举一反三,三色的问题,我们可以用三个指针来搞定。

首先,我们定义三个指针start、mid、end,分别在数组的索引-1、0和arr.length处。

荷兰国旗问题_时间复杂度_07

判断mid节点的颜色,这时候有三种情况


  1. 如果mid节点是白色的,mid节点向左移,不做任何操作。
  2. 如果mid节点是红色,与start++节点置换,并向左移。
  3. 如果mid节点是蓝色,与end–节点置换,并向左移。

整体置换的步骤如下,如果不理解建议多看两遍动画。

荷兰国旗问题_时间复杂度_08

代码

这里面0代表红色,1代表白色,2代表红色

private static void helper(int[] arr) {
int start = -1;
int end = arr.length;
int mid = 0;
while (mid < end) {
if (arr[mid] == 1) {
mid++;
continue;
}
if (arr[mid] == 0) {
arr[mid++] = arr[++start];
arr[start] = 0;
continue;
}
if (arr[mid] == 2) {
arr[mid] = arr[--end];
arr[end] = 2;
}
}
}