問題:給定整數序列,求解其中最大子序列(連續的序列)。 java
利用「分治」和遞歸的思想求解,在《數據結構與算法分析(Java語言描述)》Page29,做者給出了具體的java代碼。
整體思路是,原序列的子序列存在於三處,左、右和跨中點。將序列從中點分割,分別用遞歸求出算法
其中,步驟3分別求出包含左側和右側包含中點端點的最大子序列,求和便是結果。
這樣最後三者中的最大者即爲序列的最大子序列。數據結構
//添加了求三數最大值的函數 public class MaxSubsequence { public static int maxSubSum3(int[] a) { return maxSumRec(a,0,a.length-1); } private static int maxSumRec(int[] a,int left,int right) { if(left==right) { if(a[left]>0) return a[left]; else return 0; } int center =(left+right)/2; int maxLeftSum=maxSumRec(a, left, center); //遞歸1 int maxRightSum=maxSumRec(a, center+1, right); //遞歸2 int maxLeftBorderSum=0,leftBorderSum=0; for(int i=center;i>=left;i--) { leftBorderSum+=a[i]; if(leftBorderSum>maxLeftBorderSum) maxLeftBorderSum=leftBorderSum; } int maxRightBorderSum=0,rightBorderSum=0; for(int i=center+1;i<=right;i++) { rightBorderSum+=a[i]; if(rightBorderSum>maxRightBorderSum) maxRightBorderSum=rightBorderSum; } return max3(maxLeftSum,maxRightSum,maxLeftBorderSum+maxRightBorderSum); } public static int max3(int a,int b,int c) { int temp=a>b?a:b; int max3=temp>c?temp:c; return max3; } public static void main(String[] args) { // TODO Auto-generated method stub int[] arr={4,-3,5,-2,-1,2,6,-2}; int maxSubSum=maxSubSum3(arr); System.out.println("the maxSubSum of array arr is:"+maxSubSum); } }
程序中,maxSumRec爲遞歸函數,函數開始爲是否到達遞歸基準的if判斷語句,而後分別是兩個遞歸語句,執行步驟①②。對於遞歸而言,遞歸語句後邊的語句暫時不執行,從遞歸語句處直接返回遞歸函數開始進行遞歸,在該遞歸語句處向內遞歸,直到到達基準語句,執行return跳出遞歸。遞歸語句計算出maxLeftSum和maxRightSum。函數
函數中,遞歸語句後邊的部分計算步驟③,在for循環中,固定中點端點,分別向左右兩側循環求和,利用if語句來更新包含中心端點的maxLeftBorderSum和maxRightBorderSum。兩個變量求和便是③的最大子序列。性能
最後,求出最大者便可。spa
啓發:使用遞歸的時候,須要在遞歸語句前邊,提早設置好到達基準的條件(if,return/break),以便適時完成遞歸,跳出遞歸函數。調試
注意到,該段程序的兩條遞歸語句出如今同一個函數內,起初我對遞歸的具體執行順序理解錯誤,覺得步驟①的遞歸執行得出maxLeftSum時,以後的步驟③語句並不會執行。實際進行程序調試後,發現,該段程序雖然看起來相對簡潔,但在執行時,會執行沒必要要的語句,執行邏輯並不明瞭。code
下面進行簡單的做圖分析:
blog
注意到,遞歸函數從外層,沿着計算maxLeft的路徑,通過三次遞歸調用maxSumRec函數,到達基準,在基準層分別計算遞歸函數內部的三部分maxLeft、maxRight、左側最大子序列與右側最大子序列的和maxLeftBorderSum+maxRightBorderSum,並利用max3求出最大者返回。而後再從基準層向上,計算上一層maxRightSum,而後依次規律,逐步向上計算,最後計算出整個遞歸函數的maxLeft、maxRight和左右和,最後再執行max3函數,返回三者的最大值,獲得最終的最大子序列和。遞歸
整個過程相似二叉樹的每個節點都長三個不一樣大小的桃子,從根節點遞歸到最下層的樹葉,比較三個桃子的大小,摘取一個,再摘取同輩的其餘節點的桃子。依次向上摘,整體呈現從總到分,再從分到總的一個過程。
程序主要運行部分在maxSumRec函數,分爲if判斷基準語句、兩條遞歸語句、兩個for計算半側邊界最大子序列語句。若序列元素個數爲N,T(N)爲該算法的運行時間。
在maxSumRec函數中,無論N爲多少,if基準語句部分老是運行固定的常數時間。
參考書上的方法,使用規律遞推的方式,計算T(N)。簡化O(N)爲N,並假設N爲2的冪。T(2)=2×2,T(4)=4×3,T(8)=8×4,T(16)=16×5,若N=2^k,則T(N)=N(logN+1)=O(N log N)
整體而言,對於計算序列的最大子序列而言,書中的本算法略顯麻煩,後續會進行其餘簡潔方法的補充,本文藉此算法來深刻理解遞歸的過程。