Leetcode每日一题2021.2.26第53题:最大子序和

题目描述示例方法一:动态规划思路和算法:我们用 f(i) 代表以第 i 个数结尾的 【连续子数组的最大和】,那么我们要求的就是:因此我们只需要求出每个位置的 f(i) ,然后返回 f 数组中的最大值即可。如何求 f(i)呢?我们可以考虑 nums[i] 单独成为一段还是加入 f(i-1) 对应的那一段,于是我们可以得到如下的动态规划转移方程:考虑到 f(i) 只和 f(i-1) 相关,于是我们可以只用一个变量 pre 来维护当前 f(i) 的 f(i-1)的值是多少,从而让空间复杂度从O
摘要由CSDN通过智能技术生成

题目描述

在这里插入图片描述

示例


在这里插入图片描述

方法一:动态规划

思路和算法:

我们用 f(i) 代表以第 i 个数结尾的 【连续子数组的最大和】,那么我们要求的就是:在这里插入图片描述
因此我们只需要求出每个位置的 f(i) ,然后返回 f 数组中的最大值即可。
如何求 f(i)呢?我们可以考虑 nums[i] 单独成为一段还是加入 f(i-1) 对应的那一段,于是我们可以得到如下的动态规划转移方程:在这里插入图片描述
考虑到 f(i) 只和 f(i-1) 相关,于是我们可以只用一个变量 pre 来维护当前 f(i) 的 f(i-1)的值是多少,从而让空间复杂度从O(n)降到O(1),这有点类似滚动数组的思想。

代码:

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        pre = 0
        maxAns = nums[0]
        
        for x in nums:
            pre = max(pre + x, x)
            maxAns = max(maxAns, pre)

        return maxAns

方法二:分治(线段树)

思路和算法:

我们定义一个操作 get(a, l, r) 表示查询 a 序列 [l, r] 区间内的最大子段和,那么我们最终要求的答案就是 get(nums, 0, len(nums)-1)。分治实现此操作:左子区间[l, m],右子区间[m+1, r]。维护以下四个量:

- lSum 表示 [l, r] 内以 l 为左端点的最大子段和
- rSum 表示 [l, r] 内以 r 为右端点的最大子段和
- mSum 表示 [l, r] 内的最大子段和
- iSum 表示 [l, r] 的区间和

对于长度为1的区间 [i ,i],四个量的值都和 nums[i] 相等。对于长度大于1的区间:

- 首先最好维护的是 iSum,区间 [l, r] 的 iSum 就等于【左子区间】的 iSum 加上【右子区间】的 iSum。
 - 对于 [l, r] 的 lSum ,存在两种可能,它要么等于【左子区间】的 lSum,要么等于【左子区间】的 iSum 加上【右子区间】的 lSum,两者取大。
 - 对于 [l, r] 的 rSum,同理,它要么等于【右子区间】的 rSum,要么等于【右子区间】的 iSum 加上【左子区间】的 rSum,两者取大。
- 当计算好上面的三个量之后,就很好计算 [l, r] 的 mSum 了。我们可以考虑 [l, r] 的 mSum 对应的区间是否跨越 m——它可能不跨越 m,也就是说 [l, r] 的 mSum 可能是【左子区间】的 mSum 和【右子区间】的 mSum 中的一个;它也可能跨越 m,可能是【左子区间】的 rSum 和【右子区间】的 lSum 求和。三者取大。

这样问题就得到了解决。

代码:

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:

        class Status(object):
            lSum = rSum = mSum = iSum = 0

            def __init__(self, lSum, rSum, mSum, iSum):
                self.lSum = lSum
                self.rSum = rSum
                self.mSum = mSum
                self.iSum = iSum
            
        def pushUp(l, r):
            iSum = l.iSum + r.iSum
            lSum = max(l.lSum, l.iSum + r.lSum)
            rSum = max(r.rSum, r.iSum + l.rSum)
            mSum = max(max(l.mSum, r.mSum), l.rSum + r.lSum)
            return Status(lSum, rSum, mSum, iSum)
            
        def getInfo(a, l, r):
            if l == r:
                return Status(a[l], a[l], a[l], a[l])

            # 分治
            m = (l + r) >> 1
            lSub = getInfo(a, l, m)
            rSub = getInfo(a, m + 1, r)
            return pushUp(lSub, rSub)

        return getInfo(nums, 0, len(nums)-1).mSum
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值