Divide-and-Conquer算法的設計
設計過程分為三個階段:
Divide:整個問題劃分為多個子問題
Conquer:求解各子問題(遞歸調用正設計的算法)
Merge:合并子問題的解,形成原始問題的解
下面是幾道小題使用分治算法求解的思路。
1黑白點對
題目:
給定平面上有n個白點和n個黑點,任意三點不共線,試實際一個分治算法將每個白點與一個黑點相連,是所有連線互不相交。
思路:
將所有2n個點點按照x有小到大排序,以一條垂直于x軸的線將這些點分為大小均為n的左右兩部分,左右兩部分遞歸的進行黑白點對的匹配,由于每部分分到的黑點數與白點數不一定相等,最終返回的每部分都是互不相交的黑白點對連線以及一些單獨的黑點,或者是互不相交的黑白點對連線以及一些單獨的白點。
在合并時,若兩部分剩余的點都是黑色或都是白色,則合并完成。
若一部分剩余的是黑點,另一部分剩余的是白點,則將這些點匹配連線。每次新連接一條線時,若并不與其他線相交,則連接下一對;若與其他k對相交,可以通過k-1次交換使其不再交叉(由于不存在任意三點共線,所以必然可以通過有限次交換使其互不相交),連接下一對。直到單獨的黑點或者白點全部連接完畢。
算法設計:
Divide:按照橫坐標,將所有點分為等量的左右兩部分。
Conquer:遞歸的將左右兩部分進行黑白點對的匹配。當某部分中只有一個點或兩個同色的點時,直接返回;有兩個不同色的點時,將他們匹配。
Merge:左右兩部分中若有某部分黑白點完全匹配了,直接合并,返回;左右兩部分沒有匹配的單獨點同色,直接合并,返回;將左右兩部分沒有匹配的點異色,依次匹配,若存在交叉,則可以通過與交叉線段有限次交換使其互不相交,當所有某側單獨的點都完成匹配,返回。
時間復雜度分析:
設該算法的時間復雜度為T(2n),合并時,左右兩側單獨的點最多進行n次連線,每次連線時最多與與其相交的n-1條線段進行交換,時間復雜度為O(n^2)。
T(n)=2T(n/2)+O(n^2).
逐步遞推得到時間復雜度T(n)=O(n^2).
2求最大連續子數組
題目:
給定一個數組A[1:n],數組元素由實數構成,求A的連續子數組,使得此子數組的和最大。如:A={-2,-5,6,-2,-3,1,5,-6},結果為{6,-2,-3,1,5},和為7。設計一個分治算法,求A[1:n]的和最大連續子數組
思路:
采用二分的方法將數組從中間分為左右兩個子數組,則最大子數組必然出現在以下三種情況之一:
1)完全位于左邊的數組中。
2)完全位于右邊的數組中。
3)跨越中點,包含左右數組中靠近中點的部分。
遞歸將左右子數組再分別分成兩個數組,直到子數組中只含有一個元素,退出每層遞歸前,返回上面三種情況中的最大值。
算法設計:
Divide:將數組A劃分為左右兩個子數組AL和AR。
Conquer:遞歸的求解AL和AR的最大連續子數組。若數組中只有一個數字,最大連續子數組為這個數字本身。
Merge:AL和AR的最大連續子數組的和分別為MaxLeftSum,MaxRightSum。設從AL最右端開始的連續子序列的最大和為MaxLeftBorderSum,從AR最左端開始的連續子序列的最大和為MaxRightBorderSum,那么跨越中點的最大連續子數組的和為MaxLeftBorderSum+MaxRightBorderSum。返回MaxLeftSum,MaxRightSum,MaxLeftBorderSum+MaxRightBorderSum這三者的最大值。
時間復雜度分析:
設該算法的時間復雜度為T(n),則:
T(n)=2T(n/2)+O(n),且T(1)=1.
逐步遞推得到時間復雜度T(n)=O(nlogn).
3英文字母編碼
題目:
將26個英文字母進行編碼,‘A’編碼為‘1’,‘B’編碼為‘2’,……,‘Z’編碼為‘26’。那么給定一個數字序列可以對其進行解碼,但解碼不唯一。比如給定數字序列“234”,可以解碼為“2-3-4”,對應“BCD”;也可以解碼為“23-4”,對應“WD”。設計一個分治算法,對于給定的數字序列LIST,求出給數字序列有幾種解碼方式。
思路:
編碼為1~26,所以數字有以下幾種情況:
數字0必須與它前面的1或2一起編碼,只有一種編碼情況;數字1可單獨編碼,也可與其后面的數字一起編碼,通常有兩種情況,但當后面跟著10或20時,只能編碼為A;數字2可單獨編碼,也可與其后面的數字0~6一起編碼,但當后面跟著7、8、9、10、20時,只能編碼為B;數字3~9前沒有1或2時只有一種編碼情況。
可以遞歸的將序列LIST二分,直到每個子序列中只有一個數字,此時子序列的解碼方式為1,合并時考慮合并處兩個數字是否有更多的解碼方式。
算法設計:
Divide:將序列LIST劃分為左右兩個子序列LISTL和LISTR。
Conquer:遞歸的求解LISTL和LISTR的解碼方式。若序列中只有一個數字,返回解碼方式為1。
Merge:將LISTL和LISTR的解碼方式數相乘,得到res。再考慮合并處的兩個數字,即LISTL的最右數字n1與LISTR的最左數字n2,若n1等于1,n2不為0,則將res乘以2;若n1等于2,n2為1~6,則將res乘以2;其余情況res不變。將得到的res返回。
時間復雜度分析:
設該算法的時間復雜度為T(n),則:
T(n)=2T(n/2)+O(n),且T(1)=1.
逐步遞推得到時間復雜度T(n)=O(nlogn).
4求逆序數
題目:
設A[1:n]是由不同實數組成的數組,如果iA[j],則稱實數對(A[i],A[j])是該數組的一個反序。如,若A=[3,5,2,4],則該數組存在3個反序(3,2)、(5,2)和(5,4)。設計一個分治算法,求逆序數。
思路:
類似于歸并排序算法。先將數組從中間分成兩個部分,然后分別遞歸左半部分和右半部分,再合并排好序的左右兩個部分,從而統計逆序對數。
對于兩個排好序的數組AL和AR,初始時分別設置指針p1、p2在數組最左端,比較AL[p1]與AR[p2]的大小:如果AL[p1]>AR[p2],那么顯然AL中AL[p1]及其后面的所有元素都能與AR[p2]構成逆序對,記錄這個數并將p2右移;否則將p1右移,當完成兩個數組的遍歷后就得到了這兩個數組間的逆序數。
算法設計:
Divide:將數組A劃分為左右兩個子數組AL和AR。
Conquer:遞歸的求解AL和AR的逆序數。若數組中只有一個數字,則返回逆序數為0。
Merge:AL和AR的逆序數分別為sum1、sum2。AL和AR在之前的合并中已經按從小到大的順序排好序了,所以我們可以在一次對這兩個數組的遍歷中,將它們以歸并排序的方法合并為A時,同時得到這兩個數組間的逆序數sum3。A的逆序數為sum1+sum2+sum3。
時間復雜度分析:
每次都要將序列的的n個元素合并,時間復雜度為O(n)。
設該算法的時間復雜度為T(n),則:
T(n)=2T(n/2)+O(n),且T(1)=1.
逐步遞推得到時間復雜度T(n)=O(nlogn).
5友誼點對
題目:
給定平面上n個點構成的集合S={p1,p2,……,pn}。如果存在便平行于坐標軸的矩形僅包含S中的兩個點pi和pj(1<=i<j<=n),則稱pi和pj為友誼點對。試設計一個分治算法統計S中友誼點對的個數。
算法設計:
預處理:將點集S中的所有點按照x有小到大排序。
Divide:將點集S用一條垂直于x軸的直線l:x=m劃分為兩個大小相等的子集SL和SR。
Conquer:遞歸的求解SL和SR中友誼點對數。若點集中點的數量為1,返回友誼點對數0;若點集中點的數量為2,這兩個點一定為友誼點對,返回友誼點對數1。
Merge:S的友誼點對數=SL的友誼點對數+SR的友誼點對數+兩點分別位于SL和SR的友誼點對數。兩點分別位于SL和SR的友誼點對數的求法如下:
1)p0(x0,y0)為SL中最右點,以y=y0為界將SR分為上下兩部分討論。對于上半部分找出x最小的點A和y最小的點B,能與p0構成友誼點對的必然出現在以A、B為頂點的矩形區域中。
2)依次遍歷橫坐標在區間(xA,xB)中的點。能與p0構成友誼點對的,必然是橫坐標依次增大同時縱坐標依次減小的(若橫坐標增大的同時縱坐標也增大,后一個點與p0構成的矩形中會包含前一個點)。下半部分同理。
3)p1為SL中次右點,若y1>y0,則只需考慮y>y0的區域;反之,只需考慮y<y0的區域。對于pk,只需考慮SL中縱坐標與其相鄰的兩個點pi、pj的縱坐標所夾區域(yi,yj)。對于SL中的每個點重復上述兩步,直到完全遍歷。
時間復雜度分析:
設該算法的時間復雜度為T(n),則:
T(n)=2T(n/2)+f(n),且f(n)<=O(n^2).
所以T(n)=O(n^2).