leetcode53_最大子序和(相当于子串)

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。连续问题,其实相当于子串

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6

暴力法,
我们通过i和j记录子序列的左右边界,然后遍历所有的边界,寻找区间[i:j]和最大是多少即可。
时间复杂度O(n2) 空间复杂度 O(1)

import sys
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
    	maxsub = nums[0]
        for i in range(0, len(nums) - 1):
            mysum = 0
            for j in range(i, len(nums) - 1):
                mysum += nums[j]
                if mysum > maxsub:
                    maxsub = mysum
        return maxsub

我们这里只要求子序列的最大和,不要求得到子序列本身,其实就暗示了动态规划、分治

分治,我们把数组nums以中间位置m分为左(left)右(right)两部分, left = nums[0]…nums[m - 1] 和 right = nums[m + 1]…nums[n-1],对于每个区间 ,再像上述这样分治求解。当递归逐层深入直到区间长度缩小为1的时候,递归「开始回升」。这个时候我们考虑如何合并区间信息。
对于一个区间[l, r],我们可以维护四个变量:
lsum表示 [l, r] 内以 l 为左端点的最大子段和
rsum表示[l, r] 内以 r 为右端点的最大子段和
msum表示 [l, r] 内的最大子段和
isum表示 [l, r] 的区间和
对于长度为 1 的区间 ,四个量的值都相等。对于长度大于1 的区间:
isum: 等于「左子区间」的 isum 加上「右子区间」的 isum。
lsum: 它要么等于「左子区间」的 lsum,要么等于「左子区间」的 isum 加上「右子区间」的 lsum,二者取大。
rsum: 它要么等于「右子区间」的 rsum,要么等于「右子区间」的 rsum 加上「左子区间」的 isum,二者取大。
msum: 我们可以考它的最大字段是否跨越m——它可能不跨越 ,也就是说 msum可能是「左子区间」的 msum和「右子区间」的 msum 中的一个;它也可能跨越 ,可能是「左子区间」的 rsum 和 「右子区间」的 lsum 求和。三者取大。
这样问题就得到了解决。
时间复杂度: O(nlogn)
空间复杂度: O(logn) - 因为二分法,调用栈的深度最多是logn。
分治方法可以对任何区段【l, r】进行搜索

class Status:
    def __init__(self, lsum, rsum, msum, isum):
        self.lsum = lsum
        self.rsum = rsum
        self.msum = msum
        self.isum = isum    

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        return self.get_status(nums, 0, len(nums)-1).msum
        
    def get_status(self, nums, left, right):
        if left == right:
            return Status(nums[left], nums[left], nums[left], nums[left])
        else:
            mid = (right + left) // 2
            left_status = self.get_status(nums, left, mid)
            right_status = self.get_status(nums, mid+1, right)
            isum = left_status.isum + right_status.isum
            lsum = max(left_status.lsum, left_status.isum + right_status.lsum)
            rsum = max(left_status.rsum + right_status.isum, right_status.rsum)
            msum = max(left_status.msum, right_status.msum, left_status.rsum+right_status.lsum)
        return Status(lsum, rsum, msum, isum) 

动态规划,动态规划的难点在于找到状态转移方程,

dp[i] - 表示一定要把当前位置 i 放进子序列的时候该连续子序列的最大和可以是多少,
明显这个时候 i 这个位置的元素是该子序列的最后一个元素

状态转移方程为: dp[i] = max(dp[i - 1] + nums[i], nums[i])

初始化:dp[0] = nums[0]

从状态转移方程中,我们只关注前一个状态的值,所以不需要开一个数组记录位置所有子序列和,只需要两个变量,

currMaxSum - 当前位置 i 的最大和

maxSum - 全局最大和:

currMaxSum = max(currMaxSum + nums[i], nums[i])
maxSum = max(currMaxSum, maxSum)

时间复杂度: O(n)
空间复杂度: O(1)

 class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        cur_max = 0
        history_max = -inf
        for cur in nums:
            # 状态转移方程,当前状态的最大和
            # dp[i] = max(dp[i - 1] + nums[i], nums[i])
            cur_max = max(cur_max + cur, cur)
            # 用当前状态的最大和去更新全局最大和
            history_max = max(history_max, cur_max)
        return history_max
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值