July收集荷兰国旗问题之三路partition

这道题目和分成两块的partition的扩展。比如有一堆0 1 2 数字组成的数组,要分成 00 00  11 1 1  222 2这样的顺序的。


利用lumoto版的partition能够非常好的解决,比hoare好多了。并且直接利用loop invariant,变成i j k三个指针,[low,i]=0 [i+1,j]=1, [j+1,k-1]=2, 里面假设新来2的话,直接k++,

假设是1的话,须要和a[j+1] swap, 同一时候j++, 假设0的话。须要先和a[i+1] swap i++, 然后和 a[j+1] swap j++, 因此算法例如以下:

void NertherlandFlags(int *a, int n)
{
	int low=0,high=n-1;
	int i=low-1,j=low-1;
	for(int k=low;k<=high;k++)
	{
		if(a[k]==2) ;
		else if(a[k]==1) 
		{
			j++;
			swap(a[j],a[k]);
		}
		else if(a[k]==0)
		{
			i++;
			swap(a[i],a[k]);
			j++;
			swap(a[j],a[k]);
		}
	}
}
代码比較好写,感觉这都是一类题,

1.快排的partition。注意留一个pivot,算导是留最后一个,所以loop到high-1就停了,里面和pivot比。最后加一次swap把pivot放中间

loop invariant:  [low,i] <pivot, [i+1, j-1]>pivot, =放那边都行。exit loop时 j=high, 最后把swap跳进来

2.奇偶排序,整个区间划分为左奇右偶。和%2=0 =1比。loop 到high,
loop invariant:  [low,i] 奇, [i+1, j-1]偶, exit时 j=high+1, 包括整个区间了

3.荷兰国企问题,整个区间划分为0 1 2 (红 黄 蓝三块), loop 到high,

loop invariant:  [low,i] 0, [i+1, j] 1, [j+1, k-1]  exit时 j=high+1, 包括整个区间了


抱歉大家,这道荷兰国旗代码有bug。我才知道的。后经过分析主要是前面仅仅有2 或者0 2时,假设来了0,后面swap(a[j],a[k]) 会多交换一次使得交换回去,所以假设仅仅有0的情况。事实上不须要交换,而swap(a[j],a[k]) swap(a[j],a[k])都是子交换,因此1次两次不影响。于是统一到一起就是没有1区间的时候,也即[i+1,j]区间空,依据性质最多相差1。因此是i==j的时候出错,所以此时少一次swap, 可是i++ j++须要的 保持ij同步 

事实证明未经过OJ測试的代码非常难保证正确性。leetcode setcolor题 就是上面的挂了,于是经过多种组合条件分析发现bug。单独处理1 interval空的情况

void sortColors(int a[], int n) {
        int i=-1,j=-1;
        for(int k=0;k<=n-1;k++)
        {
            if(a[k]==0)
            {
                if(i==j)//before are 2 or 0 2, swap one is only, two will error, also 0 swap once is ok, so that is no 1 interval would only swap once  
                {
                    i++;
                    swap(a[i],a[k]);
                    j++;
                }
                else
                {
                    i++;
                    swap(a[i],a[k]);
                    j++;
                    swap(a[j],a[k]);
                }
            }
            else if(a[k]==1)
            {
                j++;
                swap(a[j],a[k]);
            }
        }
    }


附上July版本号,前面 0 1区间 最后面2区间  1 2 之间没处理的


void sortColors(int a[], int n) {
        int begin=0,end=n-1,current=0;
        while(current<=end)
        {
            if(a[current]==0)
		        swap(a[begin],a[current]),begin++, current++;
	        else if(a[current]==1)
		        current++;
	        else if(a[current]==2)
		        swap(a[current],a[end]),end--; //current not move
        }
    }
这样的不用考虑特殊情况。比方 仅仅有2 和仅仅有0 2的区间的情况,事实上也是单向扫描过来的,仅仅是调整了未处理部分和 0 1 2 区间的顺序而已,由于指针从current+1, 到end处理了。一个

指针扫描的,还是单向扫描好。事实上也不是Hoart版啦,所以自己不用管Hoard版,坚持Lumoto版就能够了,sumous_t大神似乎也是的。


似乎另一种改进第一个版本号的思路。就是不swap,而是覆盖,像优化partition一样的

附上July博客 https://github.com/bolpigo/The-Art-Of-Programming-By-July/blob/master/ebook/zh/02.07.md

sumous_t 大神代码: https://github.com/julycoding/The-Art-Of-Programming-By-July/blob/master/ebook/code/python/2.8:%20%E8%8D%B7%E5%85%B0%E5%9B%BD%E6%97%97%E9%97%AE%E9%A2%98.py


再附上sumous_t大神帮我改动后AC的代码,膜拜下大神,还是女博士哦:)

void sortColors(int a[], int n) {
        int low=0,high=n-1;  
    int i=low-1,j=low-1;  
    for(int k=low;k<=high;k++)  
    {  
        if(a[k]==2) ;  
        else if(a[k]==1)   
        {  
            j++;  
            swap(a[j],a[k]);  
        }  
        else if(a[k]==0)  
        {  
			
	    i++;j++;swap(a[j],a[k]);swap(a[j],a[i]);

        }  
    }