给定一个整数数组 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; }