这道题目和分成两块的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]);
}
}