力扣题目链接:https://leetcode.cn/problems/maximum-subarray/
给你一个整数数组 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)
的解法,尝试使用更为精妙的 分治法 求解。
使用动态规划的话思路比较简单,使用一个变量
c
n
t
cnt
cnt记录以当前元素为结尾的最大子数组和
。
这样,我们只需要遍历一遍 n u m s nums nums数组,使用公式 c n t = max ( c n t + n u m s [ i ] , n u m s [ i ] ) cnt = \max(cnt + nums[i], nums[i]) cnt=max(cnt+nums[i],nums[i])维护 c n t cnt cnt,并记得更新答案的最大值即可。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int ans = nums[0];
int cnt = nums[0];
for (int i = 1; i < nums.size(); i++) {
cnt = max(cnt + nums[i], nums[i]);
ans = max(ans, cnt);
}
return ans;
}
};
# from typing import List
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
ans, cnt = nums[0], nums[0]
for i in range(1, len(nums)):
cnt = max(cnt + nums[i], nums[i])
ans = max(ans, cnt)
return ans
写一个函数 g e t ( n u m s , l , r ) get(nums, l, r) get(nums,l,r),返回 n u m s nums nums数组从 l l l到 r r r的子数组的:
最大子数组和
最大子数组和
最大子数组和
那么,我们就可以愉快地进行递归啦!
对于 g e t ( n u m s , l , r ) get(nums, l, r) get(nums,l,r),我们可以分别求出 g e t ( n u m s , l , ⌊ l + r 2 ⌋ ) get(nums, l, \lfloor\frac{l + r}{2}\rfloor) get(nums,l,⌊2l+r⌋)(记为 l S t a t u s lStatus lStatus)和 g e t ( n u m s , ⌊ l + r 2 ⌋ + 1 , r ) get(nums, \lfloor\frac{l + r}{2}\rfloor + 1, r) get(nums,⌊2l+r⌋+1,r)(记为 r S t a t u s rStatus rStatus)。递归终止条件为 l = r l=r l=r(只有单个元素)。
于是就有:
最终返回 g e t ( n u m s , 0 , l e n ( n u m s ) − 1 ) . M S u m get(nums, 0, len(nums) - 1).MSum get(nums,0,len(nums)−1).MSum即可。
struct Status {
int lSum, rSum, MSum, iSum;
};
class Solution {
private:
Status get(vector<int>& a, int l, int r) { // get[l, r]
if (l == r) {
return {a[l], a[l], a[l], a[l]};
}
int m = (l + r) >> 1;
Status lStatus = get(a, l, m);
Status rStatus = get(a, m + 1, r);
return {
max(lStatus.lSum, lStatus.iSum + rStatus.lSum),
max(rStatus.rSum, lStatus.rSum + rStatus.iSum),
max(lStatus.MSum, max(rStatus.MSum, lStatus.rSum + rStatus.lSum)),
lStatus.iSum + rStatus.iSum
};
}
public:
int maxSubArray(vector<int>& nums) {
return get(nums, 0, nums.size() - 1).MSum;
}
};
# from typing import List
class Status:
def __init__(self, lSum: int, rSum: int, MSum: int, iSum: int) -> None:
self.lSum = lSum
self.rSum = rSum
self.MSum = MSum
self.iSum = iSum
class Solution:
def get(self, nums: List[int], l: int, r: int) -> Status:
if l == r:
return Status(nums[l], nums[l], nums[l], nums[l])
m = (l + r) >> 1
lStatus = self.get(nums, l, m)
rStatus = self.get(nums, m + 1, r)
return Status(
max(lStatus.lSum, lStatus.iSum + rStatus.lSum),
max(rStatus.rSum, lStatus.rSum + rStatus.iSum),
max(lStatus.MSum, rStatus.MSum, lStatus.rSum + rStatus.lSum),
lStatus.iSum + rStatus.iSum
)
def maxSubArray(self, nums: List[int]) -> int:
return self.get(nums, 0, len(nums) - 1).MSum
"""为何不用切片作为参数?
>>> a = [1, 2, 3]
>>> a
[1, 2, 3]
>>> b = a[1:2]
>>> b
[2]
>>> b[0] = 99
>>> a
[1, 2, 3]
>>> b
[99]
"""
相较于方法一,方法二的时间复杂度没有提升,空间复杂度反而更高了。那么方法二的意义何在?
这道题只问了“整个数组的”最大子数组和。但是如果某天遇到了一道题,问你 1 0 5 10^5 105次且每次随机问一个 [ l , r ] [l, r] [l,r]的最大子数组和 呢?
那么我们使用方法二,并且将每层的结果记录下来,就能做到每次查询都在 O ( log n ) O(\log n) O(logn)的时间复杂度下返回结果。
这就是没有懒标记的线段树。
同步发文于CSDN,原创不易,转载经作者同意后请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/134504375