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

假如有一个数组,里面的元素由红、白、蓝三个颜色组成,请你写出一个算法,使数组的元素按照红、白、蓝顺序分布。
荷兰国旗问题_数组
至于为什么叫荷兰国旗,你对比一下,荷兰国旗的三色分布就知道了。

荷兰国旗问题_数组_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–节点置换,并向左移。

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

荷兰国旗问题_java_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;
            }
        }
    }