输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。
示例1:
输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
本文选用分治法的思想解题,其实可以使用动态规划的。
分而治之
分治算法的解题思路:先将问题分解为子问题;解决子问题后,再将子问题合并,解决主要问题。
使用分治法解此题:
- 将数组分为2部分。例如[1,2,3,4]被分为[1,2]和[3,4];
- 通过递归计算,得到左右两部分的最大子序列和是leftSum,rightSum;
- 从数组中间开始从左右计算最大子序列crossSum;
- 返回max(leftSum,rightSum,crossSum);
/*
分治法
分治模板:
1.定义基本问题;
2.将问题分解为子问题并递归解决子问题;
3.合并子问题的解以获得原始问题的解;
将nums由中点mid分为三种情况:
1.最大子串在左边;
2.最大子串在右边;
3.最大子串跨中点,左右都有;
当子串在左边或右边时,继续分中点递归分解到一个数为止,对于递归后横跨的子串,
再分治为左侧和右侧求最大子串,可使用贪心算法求最大子串值,再合并为原始的最大
子串值
*/
class Solution {
public:
int maxSubArray(vector<int>& nums){
assert(!nums.empty());
return helper(nums,0,nums.size()-1);
}
int helper(vector<int>& nums,int left,int right){
if(left==right) return nums[left];//分解到一个值时返回该值
int mid=left+(right-left)/2;
int leftSum=helper(nums,left,mid);
int rightSum=helper(nums,mid+1,right);
int croSum=crossSum(nums,left,mid,right);//横跨中点的最大值
return max(max(leftSum,rightSum),croSum);
}
int crossSum(std::vector<int>& nums,int left,int mid,int right){
if(left==right) return nums[left];
//贪心算法求左边的最大值
int leftSubsum=INT_MIN;
int curSum=0;
for(int i=mid;i>left-1;--i){
curSum+=nums[i];
leftSubsum=max(leftSubsum,curSum);
}
//贪心算法求右边最大值
int rightSubsum=INT_MIN;
curSum=0;
for(int i=mid+1;i<right+1;++i){
curSum+=nums[i];
rightSubsum=max(rightSubsum,curSum);
}
return leftSubsum+rightSubsum;
}
};