算法通关村第三关——彻底弄懂荷兰国旗问题

137 阅读2分钟

1.荷兰国旗问题

对应leetcode75. 颜色分类

解题思路:

首先数组中元素有三种 0,1,2 分别代表三种不同的颜色;红,白,蓝;

我们需要选定其中一种元素作为对照值,为什么要选择对照值呢?

比如说,有一个数组:[10,3,2,6,5,11,12,20] 如果我们选择一个对照值 6 ,那么很快就能对数组中的乱序元素进行分类;

将小于6的数放在数组的左边,大于6的数放在 6 的右侧,中间部分放等于6的数值;三个区域就划分好了;小于区,等于区,大于区;

小于区中的数全部是小于对照值的;大于区的数全部是大于对照值的,等于区的数全部是等于对照值的;

荷兰国旗问题就可以参考以上示例,来进行分类;

数组中总共就3种不同的数,0,1,2;很显然选择 1 作为对照值,才能将数组中的各个元素分类;

好了,现在我们已经选择好对照值了,要如何进行分类呢?这才是问题的关键;

给定一个数组[0, 2, 0, 1, 1, 2, 0 ] 我们要如何分类呢?

定义一个小于区的边界left, 定义一个大于区的边界right;

image.png

image.png

image.png

image.png

image.png 最终数组会变成

image.png

综上可以看出无非就是这个套路

  1. 只要 i 下标位置的值小于对照值,只需将小于区left 右边一位的值和 i 做交换即可; 即swap(arr,left+1,i); 然后小于区外扩,left++,i++;
  2. 只要 i 下标位置的值等于对照值,小于区和大于区啥也不干,只需要 i++即可;
  3. 只要 i 下标位置的值大于对照值,那么要将 i 位置的值,和大于区左边一位的值和 i 做交换即可;swap(arr,i,right-1);然后大于区外扩,right--,但 i 不变 (因为新交换过来的值还没有和对照值比较过);

代码如下:

public static void sortColors(int[] nums) {
    int left=-1; //小于区
    int right=nums.length; // 大于区
    int count=0; // 记录等于区的长度
    // 循环退出的条件小于区长度+等于区长度等于right;小于区长度为left+1,等于区长度为count,大于区下标为right
    for(int i=0; left!=right-1-count;){ 
        // 小于的情况
        if(nums[i]<1){
            swap(nums,++left,i++);
        }else if(nums[i]==1){ // 等于的情况
            i++;
            count++;
        }else{ // 大于的情况
            swap(nums,i,--right);
        }
    }
}
// 交换数组中两个位置的值
public static void swap(int[] arr,int left,int right){
    int temp=arr[left];
    arr[left]=arr[right];
    arr[right]=temp;
}

荷兰国旗问题很重要,彻底弄懂它,才能对付好快速排序的问题; 它的分类思想同样适用于快排;需要彻底攻克它,才能学好快速排序;