本总结是是个人为防止遗忘而作,不得转载和商用。
问题:现有红、白、蓝三个不同颜色的小球,乱序排列在一起,请重新排列这些小球,使得红白蓝三色的同颜色的球在一起。
PS:之所以叫荷兰国旗问题,是因为可以将红白蓝三色小球想象成条状物,有序排列后正好组成荷兰国旗,如下图:
为了解决此问题,所以问题转换为:给定数组A[0…N-1],元素只能取0、1、2三个值,设计算法,使得数组排列成“00…0011…1122…22”的形式。
然后借鉴快速排序中partition的过程。定义三个指针:begin=0、cur=0、end=N-1,接下来进行判断:
A[cur]==0时:
若begin==cur,则begin++,cur++
若begin≠cur,则说吗A[begin]一定不是0,而我们的目的是0在最前面,所以A[cur]与A[begin]交换,begin++,cur++
A[cur]==1时:因为上面的判断可以保证所有的0都在最前面,所以这里直接cur++,begin不变,end不变就好。
A[cur]==2时:A[cur] 与A[end]交换,end--,cur不变,这样2就一定能在最后
写成代码的话就是:
while(cur<= end) {
if(a[cur] == 2){
swap(a[end],a[cur]);
end--;
}else if (a[cur] == 1) {
cur++;
}else {
if( begin == cur ) {
begin++;
cur++;
} else {
swap(a[begin], a[cur] );
begin++;
cur++;
}
}
}
一开始给数组添加了begin,cur, end是可以把数组分成4个空间的,如下:
A [0, begin)
B [begin, cur)
C [cur, end)
D [end, N-1]
而一开始begin,cur都指向开始,end指向0,所以A, B, D一开始时 ∅。
当然不管什么时候A, B,D这三个区间都是我们已经控制住的区域,因为:
A中一定都是0;
B中一定都是1(因为如果begin和cur都是0,则它们都会向后移动一位,于是只要begin和cur分开,则begin一定指向了1(PS:begin后面可能还有0));
D中一定都是2;
只有C才是有待考察的。
于是C中如果是0,就扔到A中,是1就扔到B中,是2就扔到D中,即:遍历cur,根据a[cur]的值做相应处理,直到区间[cur,end]为空,即cur==end时退出。
话又说回来,荷兰国旗问题中只有0,1,2三个数,那我们能不能这样:
从头到尾扫一遍,记录下0,1,2的数量,假设有八个0,五个1,3个2,那我们就直接把前八个清为0,中间五个清为1,后面3个清为2。
当然可以,不过这样干太不讲理了,而且这个实际作用不大,比如,可以使用荷兰国旗问题优化快速排序(见下面)。
当然如果全是int是能这样做的(┑( ̄Д  ̄)┍)。
如果这么定义:
A[i]< key时相当于“荷兰国旗问题”中的0
A[i]= key时相当于“荷兰国旗问题”中的1
A[i]> key时相当于“荷兰国旗问题”中的2
这样就可以使用“荷兰国旗问题”的解法来解决快速排序了,这样一来,即使待排序的元素中有一些元素和key一样,也能保证时间复杂度是最差是NlogN的,因为对于待排序的等于Key的数值,可以在执行下一次Partition时直接跳过,利于数据规模的降低。