荷兰国旗问题(改造快速排序)

来源:互联网 发布:fzltchjw gb1 0 mac 编辑:程序博客网 时间:2024/05/01 17:51

本总结是是个人为防止遗忘而作,不得转载和商用。

题目

         问题:现有红、白、蓝三个不同颜色的小球,乱序排列在一起,请重新排列这些小球,使得红白蓝三色的同颜色的球在一起。

         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时直接跳过,利于数据规模的降低。

 

 

0 0
原创粉丝点击