給定一個整數數組 nums ,找到一個具備最大和的連續子數組(子數組最少包含一個元素),返回其最大和。ios
輸入: [-2,1,-3,4,-1,2,1,-5,4],
輸出: 6
解釋: 連續子數組 [4,-1,2,1] 的和最大,爲 6。
思路:數組
將原數組分紅左右兩部分,元素數都爲n/2,令最大子數組的下標爲i,j則i和j的狀況分爲三種:
cors
- i,j都屬於原數組的左半側,即low<=i<=j<=mid
- i,j都屬於原數組的有半側,即mid<i<=j<=high
- i,j跨越mid點,即low<=i<=mid<j<=high
則根據上述的三種狀況創建分治策略,分別求解左側部分的最大子數組、右側部分的最大子數組和跨越mid的最大子數組,而後比較上述三個最大子數組取最大值,即爲給定數組的最大子數組curl
僞碼:優化
//求解跨越mid的最大子數組 //因爲i,j跨越mid,則左側的末尾元素必定是mid,右側的起始元素必定是mid+1 FIND-MAX-CROSSING-SUBARRAY(A,low,mid,high) //跨越mid左側部分的最大值 left-sum=MIN //初始值爲最小值 sum=0 max-left//存儲下標 for i=mid to low sum=A[i]+sum if sum>left-sum max-left=i left-sum=sum //跨越mid右側部分的最大值 right-sum=MIN sum=0 max-right for j=mid+1 to high sum=A[j]+sum if sum>right-sum max-right=j right-sum=sum return(max-left,max-right,left-sum+right-sum) //求數組A的最大子數組 FIND-MAXIMUM-SUBARRAY(A,low,high) if low==high return(low,high,A[low])//只有一個元素的狀況,返回當前元素的位置和值 else mid=(low+high)/2 (left-low,left-high,left-sum)=FIND-MAXIMUM-SUBARRAY(A,low,mid) (right-low,right-high,right-sum)=FIND-MAXIMUM-SUBARRAY(A,mid+1,high) (corss-low,cross-high,cross-sum)=FIND-MAX-CROSSING-SUBARRAY(A,low,mid,high) if left-sum>right-sum and left-sum>corss-sum return (left-low,left-high,left-sum) else if right-sum>left-sum and right-sum>corss-sum return (right-low,right-high,right-sum) else return (cross-low,cross-high,cross-sum)
源碼實現:url
#include <iostream> #include <tuple> #include <limits.h> using namespace std; typedef tuple<int,int,long long> resultType; resultType FindMaxCrossSubarray (int *A,int low,int mid,int high) { int left_max =mid; long long leftMaxSum = LLONG_MIN; long long sum=0; for(int i = mid;i>=low;--i) { sum=sum+A[i]; if(sum>leftMaxSum) { left_max = i; leftMaxSum = sum; } } int right_max = mid+1; long long rightMaxSum=LLONG_MIN; sum = 0; for(int j=mid+1;j<=high;++j) { sum=sum+A[j]; if(sum>rightMaxSum) { right_max = j; rightMaxSum = sum; } } return {left_max,right_max,leftMaxSum+rightMaxSum}; } resultType FindMaxSubArray(int *A,int low,int high) { if(low == high) { return {low,high,A[low]}; } else { int mid = (low+high)/2; resultType leftResult = FindMaxSubArray(A,low,mid); resultType rightResult = FindMaxSubArray(A,mid+1,high); resultType crossResult = FindMaxCrossSubarray(A,low,mid,high); if(get<2>(leftResult)>get<2>(rightResult)&&get<2>(leftResult)>get<2>(crossResult)) { return leftResult; } else if(get<2>(rightResult)>get<2>(leftResult)&&get<2>(rightResult)>get<2>(crossResult)) { return rightResult; } else { return crossResult; } } } int main() { int test[9]{-2,1,-3,4,-1,2,1,-5,4}; resultType result = FindMaxSubArray(test,0,sizeof(test)/sizeof(int)-1); for(int i=get<0>(result);i<=get<1>(result);i++) { cout<<test[i]<<ends; } cout<<endl<<get<2>(result); system("pause"); return 0; }
思路爲改變計算跨越mid的最大子數組的方法。[l...r]這個區間的最大子數組可能爲[l...m]的最大子數組和[m+1...r]的最大子數組,或包含m和m+1的最大子數組(即跨越mid)。統計四個量,分別爲:spa
則在合併時根據左、右兩側的統計量更新合併後的統計量,最終獲得的msum即爲最大子數組。更新規則爲code
僞碼實現:get
FIND-MAX-SUBARRAY(A,low,high) if low==high return (A[low],A[low],A[low],A[low])//四個數分別問lsum,rsum,isum,msum else mid = (low+high)/2 (l_lsum,l_rsum,l_isum,l_msum)=FIND-MAX-SUBARRAY(A,low,mid) (r_lsum,r_rsum,r_isum,r_msum)=FIND-MAX-SUBARRAY(A,mid+1,high) lsum=l_lsum>(l_isum+r_lsum) ? l_lsum :l_isum+r_lsum rsum=r_rsum>(r_isum+l_rsum) ? r_rsum:r_isum+l_rsum isum=l_isum+r_isum if l_msum>r_msum and l_msum>(l_rsum+r_lsum) return (lsum,rsum,isum,l_msum) if r_msum>l_msum and r_msum>(l_rsum+r_lsum) return (lsum,rsum,isum,r_msum) else return (lsum,rsum,isum,l_rsum+r_lsum)
源碼實現:如下源碼只求出的最大子數組的和,沒有保存最大子數組對應的下標源碼
#include <iostream> #include <tuple> using namespace std; using resultType = tuple<long long,long long,long long,long long>; resultType FindMaxSubArray(int *A,int low,int high) { if(low==high) { return {A[low],A[low],A[low],A[low]}; } else { int mid = (low+high)/2; resultType lResult =FindMaxSubArray(A,low,mid); resultType rResult =FindMaxSubArray(A,mid+1,high); int l_lsum = get<0>(lResult), l_rsum = get<1>(lResult), l_isum = get<2>(lResult), l_msum = get<3>(lResult); int r_lsum = get<0>(rResult), r_rsum = get<1>(rResult), r_isum = get<2>(rResult), r_msum = get<3>(rResult); int lsum = l_lsum > (l_isum+r_lsum) ? l_lsum :l_isum+r_lsum; int rsum = r_rsum > (r_isum+l_rsum) ? r_rsum:r_isum+l_rsum; int isum = l_isum+r_isum; if(l_msum>r_msum && l_msum>(l_rsum+r_lsum)) { return {lsum,rsum,isum,l_msum}; } else if(r_msum>l_msum && r_msum>(l_rsum+r_lsum)) { return {lsum,rsum,isum,r_msum}; } else { return {lsum,rsum,isum,l_rsum+r_lsum}; } } } int GetMaxSubArrayValue(int *A,int low,int high) { resultType result = FindMaxSubArray(A,low,high); return get<3>(result); } int main() { int test[9]{-2,1,-3,4,-1,2,1,-5,4}; int result = GetMaxSubArrayValue(test,0,sizeof(test)/sizeof(int)-1); cout<<endl<<result; system("pause"); return 0; }
僞碼:
FIND-MAX-SUBARRAY(A,low,high) cursum=MIN//用於保存當前求和的結果 curleft=0//用於保存當前的計算的可能的最大子數組的左邊界 maxsum=MIN//用於保存已遍歷部分的最大和 maxleft=0//保存已遍歷部分的最大和對應的左邊界 maxright=0//保存已遍歷部分的最大和對應的右邊界 for i=low to high if cursum<0 cursum=A[i] curleft=i else cursum=cursum+A[i] if cursum>maxsum maxsum=cursum maxleft=curleft maxright=i return (maxsum,maxleft,maxright)
源碼實現:
#include <iostream> #include <tuple> #include <limits.h> using namespace std; using ResultType=tuple<long long,int,int>; ResultType FindMaxSubArray(int *A,int low,int high) { long long cursum = LLONG_MIN; int curleft = 0; long long maxsum = LLONG_MIN; int maxleft = 0; int maxright = 0; for(int n=low;n<=high;++n) { if(cursum<0) { cursum=A[n]; curleft=n; } else { cursum+=A[n]; } if(cursum>maxsum) { maxsum = cursum; maxleft = curleft; maxright = n; } } return {maxsum,maxleft,maxright}; } int main() { int test[9]{-2,1,-3,4,-1,2,1,-5,4}; ResultType result = FindMaxSubArray(test,0,sizeof(test)/sizeof(int)-1); for(int i=get<1>(result);i<=get<2>(result);i++) { cout<<test[i]<<ends; } cout<<endl<<get<0>(result); system("pause"); return 0; }