-
给你一个整数数组 nums ,请你找出一个具有
最大和的连续子数组
(子数组最少包含一个元素),返回其最大和。 -
子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入:nums = [1]
输出:1
示例 3:
输入:nums = [5,4,-1,7,8]
输出:23
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的 分治法 求解。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
}
};
题解
暴力解
- 暴力解法就是遍历所有的子数组
- 前缀和的话也需要 O ( N 2 ) O(N^2) O(N2)复杂度
前缀和+前缀最小值:
- 利用前缀和来求最大和连续子数组主要思路就是求出nums数组地前缀和数组sum,若就此想要求出最大和的连续子数组就需要两个for循环,我们可以利用pre[i]数组来记录1-i之间最小地前缀和,这样只需要一个for就可以求出答案,要注意的是,最大和地连续子数组不能为null ,所以ans=max(ans,sum[i]−pre[i−1])
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int n=nums.size();
vector<long long>sum(n+1,0);
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+nums[i-1];
vector<long long>pre(n+1,0);
long long ans=-1e10;
pre[0]=sum[0];
for(int i=1;i<=n;i++)pre[i]=min(pre[i -1],sum[i]);
for(int i=1;i<=n;i++)ans=max(ans,sum[i]-pre[i-1]);
return ans;
}
};
dfs
- 当前操作:枚举小于 i 的子数组选或不选
- 子问题:从前 i 个数中得到的最大和
- 下一个子问题:分类讨论:
不选(子数组小于0):从当前数开始计算子数组和
选(子数组大于0):加上当前数继续作为子数组的和
class Solution {
public:
int maxSubArray(vector<int>& nums) {
// // 记忆化搜索
// int n = nums.size(), cache[n];
// memset(cache, -1, sizeof(cache));
// function<int(int)> dfs = [&](int i) {
// if ( i<0 )
// return 0;
// if ( cache[i]!=-1 )
// return cache[i];
// return cache[i] = dfs(i-1)>0 ? dfs(i-1)+nums[i] : nums[i];
// };
// dfs(n-1);
};
分治
class Solution {
public:
struct Status {
int lSum, rSum, mSum, iSum;
};
Status pushUp(Status l, Status r) {
int iSum = l.iSum + r.iSum; ////[l,r] 内以所有和
int lSum = max(l.lSum, l.iSum + r.lSum);//[l,r] 内以 l 为左端点的最大子段和
int rSum = max(r.rSum, r.iSum + l.rSum);//[l,r] 内以 r 为右端点的最大子段和
int mSum = max(max(l.mSum, r.mSum), l.rSum + r.lSum);
return (Status) {lSum, rSum, mSum, iSum};
};
Status get(vector<int> &a, int l, int r) {//查询 [l,r] 区间内的最大子段和
if (l == r) {
return (Status) {a[l], a[l], a[l], a[l]};
}
int m = (l + r) >> 1;
//[l,m] 和[m+1,r] 分治求解
Status lSub = get(a, l, m);
Status rSub = get(a, m + 1, r);
return pushUp(lSub, rSub);
}
int maxSubArray(vector<int>& nums) {
return get(nums, 0, nums.size() - 1).mSum;
}
};
//作者:LeetCode-Solution 链接:https://leetcode.cn/problems/maximum-subarray/solution/zui-da-zi-xu-he-by-leetcode-solution/
- 分治和二分
两者一般都会用到int mid=left+(right-left)/2;
处理。但二分算法比较之后,会选择其中一个分支。分治只是做了划分,两个分支都要执行。