1. 程式人生 > >荷蘭國旗問題(改造快速排序)

荷蘭國旗問題(改造快速排序)

題目

         問題:現有紅、白、藍三個不同顏色的小球,亂序排列在一起,請重新排列這些小球,使得紅白藍三色的同顏色的球在一起。

         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時直接跳過,利於資料規模的降低。