(LeetCode 53)Maximum Subarray
Find the contiguous subarray within an array (containing at least one number) which has the largest sum.
For example, given the array [−2,1,−3,4,−1,2,1,−5,4]
,
the contiguous subarray [4,−1,2,1]
has the largest sum = 6
.
题目:
最大子数组和
思路:
1、暴力枚举
起点:i=0,...,n-1;终点:j=i,....,n-1;依次求[i,j]区间的和,时间复杂度O(n^3)
2、优化枚举
起点:i=0,...,n-1;终点:j=i,....,n-1;累计求[i,j]区间的和,时间复杂度O(n^2)
3、分治算法
分:两个等长的子数组,分别求解,复杂度O(nlogn)
合:求包含中间点的最大子数组之和,复杂度O(n)
时间复杂度:O(nlogn)
4、动态规划
假设dp[i]表示以a[i]结尾的最大子数组和,那么
状态转移方程:
dp[i]=max(dp[i-1]+a[i],a[i])
- 包含a[0,i-1]:dp[i-1]+a[i]
- 不包含a[0,i-1]:a[i]
初始值:
dp[0]=a[0]
复杂度:
时间复杂度:O(n),空间复杂度:O(n)
空间优化:
dp[i]只与dp[i-1]有关,因此状态转移方程优化为:
best=max(best+a[i],a[i])
其实这里的动态规划实现的是一种简单的逻辑,即前面的数组和大于0,则加上,小于或等于0,则放弃。
if(cur>0)
cur+=A[i];
else
cur=A[i];
5、前缀数组和
定义:sum[i]=a[0]+a[1]+...+a[i]
sum(A[i....j])=sum[j]-sum[i-1]
对于数组A,以A[i]结尾的最大子数组和为sum[i]-min(sum(k)),k=0...i-1,因此需保存每一步计算中的最小sum值。
依次计算以A[i]结尾的最大子数组和,然后保留其最大值即可,详见代码。
代码:
只实现分治、动态规划以及前缀和三种思路
1、分治
class Solution { public: int maxSubArray(int A[], int n) { if(n==1) return A[0]; int mid=n/2; int left=maxSubArray(A,mid); int right=maxSubArray(A+mid,n-mid); int ans=max(left,right); int cur=A[mid-1]; int tmp=cur; for(int i=mid-2;i>=0;i--){ cur+=A[i]; if(cur>tmp) tmp=cur; } cur=tmp; for(int i=mid;i<n;i++){ cur+=A[i]; if(cur>tmp) tmp=cur; } return max(ans,tmp); } };
2、动态规划
class Solution { public: int maxSubArray(int A[], int n) { int cur=A[0]; int max=A[0]; for(int i=1;i<n;i++){ if(cur>0) cur+=A[i]; else cur=A[i]; if(cur>max) max=cur; } return max; } };
class Solution { public: int maxSubArray(int A[], int n) { int endhere=A[0]; int ans=A[0]; for(int i=1;i<n;i++){ endhere=max(endhere+A[i],A[i]); ans=max(ans,endhere); } return ans; } };
3、前缀数组和
class Solution { public: int maxSubArray(int A[], int n) { int sum=A[0]; int minSum=min(0,sum); int ans=A[0]; for(int i=1;i<n;i++){ sum+=A[i]; ans=max(ans,sum-minSum); minSum=min(minSum,sum); } return ans; } };