题目描述: 最大子序和
给定一个序列(至少含有 1 个数),从该序列中寻找一个连续的子序列,使得子序列的和最大。例如,给定序列 [-2,1,-3,4,-1,2,1,-5,4],
连续子序列 [4,-1,2,1] 的和最大,为 6。
扩展练习:
若你已实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
解:使用动态规划规划的思想,求一个序列中连续子序列的最大和,定义状态,定义一个数组dp[i],表示以第i个元素为结尾的当前连续子序列的最大和。dp[0]--dp[i-1]表示已经求出的以i-1为结尾的最大子序和。如果dp[i-1]<0,那么dp[i]加上前面长度任意子序和都会小于不加前面的子序和。状态转移方程为:
dp[i]=nums[i] if dp[i-1]<=0
dp[i]=dp[i-1]+nums[i] if dp[i-1]>0
class Solution {
public:
int maxSubArray(vector<int>&nums){
int len=nums.size();
if(len==0)
return 0;
if(len==1)
return nums[0];
vector<int> dp(len,0);
dp[0]=nums[0];
int maxNum=nums[0];
for(int i=1;i<len;i++){
if(dp[i-1]>0)
dp[i]=dp[i-1]+nums[i];
else
dp[i]=nums[i];
maxNum=max(dp[i],maxNum);
}
return maxNum;
}
};
分治法:
思路:假设数组下标有效范围是l到r,将数组分为左半部分下标为(l,mid-1)和右半部分下标为(mid+1,r)以及中间元素下标为mid,接下来递归求出左半部分的最大子序和:left=maxNum(nums,l,mid-1); 右半部分最大子序和right=maxNum(nums,mid+1,r);
接下来再将左半部分右边界,右半部分左边界以及中间元素nums[mid]整合,用了两个循环,先整合左半部分右边界和中间值,再将整合结果与右半部分左边界整合得到整合以后的最大子序和max_num,最后返回max_num,left,right的最大值即是要求的最大子序和。
class Solution {
public:
int maxSubArray(vector<int>&nums)
{
if(nums.size()==0)
return 0;
return maxSum(nums,0,nums.size()-1);
}
int maxSum(vector<int>&nums,int l,int r)
{
if(l>r)
return INT_MIN;
if(l==r)
return nums[l];
int mid=(l+r)/2;
int leftMaxNum=maxSum(nums,l,mid-1);
int rightMaxNum=maxSum(nums,mid+1,r);
int maxNum=nums[mid];
int temp=nums[mid];
for(int i=mid-1;i>=l;i--){
temp+=nums[i];
maxNum=max(maxNum,temp);
}
temp=maxNum;
for(int j=mid+1;j<=r;j++){
temp+=nums[j];
maxNum=max(maxNum,temp);
}
return max(max(leftMaxNum,rightMaxNum),maxNum);
}
};