53. 最大子序和

题目描述

53. 最大子序和

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

示例:

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

解题方法

尝试1:暴力搜索,遍历所有可能的区间组合

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        dp = [[0 for j in range(len(nums))] for i in range(len(nums))]
        for i in range(len(nums)):
            dp[i][i] = nums[i]
        max_sum = nums[0]
        for i in range(len(nums)):
            max_sum_i = dp[i][i]
            for j in range(i+1,len(nums)):
                dp[i][j] = dp[i][j-1] + nums[j]
                max_sum_i = max(max_sum_i,dp[i][j])
            max_sum = max(max_sum,max_sum_i)
        return max_sum

时间复杂度 O ( n 2 ) O(n^2) O(n2)

尝试2:积分图避免重复求和计算,遍历所有可能区间找最大

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        dp = [[0 for j in range(len(nums))] for i in range(len(nums))]
        dp = [0 for i in range(len(nums))]
        dp[0] = nums[0]
        for i in range(1,len(nums)):
            dp[i] = dp[i-1] + nums[i]
        dp = [0] + dp
        max_sum = max(nums)
        for i in range(len(nums)):
            for j in range(i+1,len(nums)):
                sum_ij = dp[j+1] - dp[i]
                max_sum = max(max_sum,sum_ij)
        return max_sum

时间复杂度 O ( n 2 ) O(n^2) O(n2)

一维dp

状态定义

dp[i]代表第i位结尾的子串的最大自序和

状态转移

dp[i] = max(dp[i-1]+nums[i],nums[i])

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        dp = [nums[0] for i in range(len(nums))]
        for i in range(1,len(nums)):
            dp[i] = max(dp[i-1]+nums[i],nums[i])
        return max(dp)
思路点评

按照暴力搜索方法,需要遍历子串的首和尾 O ( n 2 ) O(n^2) O(n2)种组合。该方法非常巧妙,只遍历子串的末位置,因为存储了上一位置数字为结尾的最大自序和,接上当前位置数字后,最大子序和只能是上一位置结果+当前数字结果与当前数字中较大的一个。这样一来不关注子串的起始位置,很巧妙的将时间复杂度变为了O(n)。

贪心法

每一步中如果加上当前元素子串和变为负数,说明后续搜索中最大和子串一定不包含当前元素之前的部分。

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        max_num = nums[0]
        sub_sum = 0
        for i in range(len(nums)):
            sub_sum += nums[i]
            max_num = max(max_num, sub_sum)
            if sub_sum < 0:
                sub_sum = 0
        return max_num

分治

将序列分出两半,最大子序和的子串存在位置如下情况:

  1. 左半区间
  2. 右半区间
  3. 横跨左半区间和右半区间

横跨两区间的最大子序和=从左半区间右端逆序遍历找到最大子序和+右半区间左端顺序遍历最大子序和
时间复杂度 O(nlogn)

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        n = len(nums)
        if n <= 2:
            return max(nums+[sum(nums)])
        max_left = self.maxSubArray(nums[:n//2])
        max_right = self.maxSubArray(nums[n//2:])

        tmp = nums[n//2-1]
        max_l = nums[n//2-1]
        for i in range(n//2-2,-1,-1):
            tmp += nums[i]
            max_l = max(max_l,tmp)

        tmp = nums[n//2]
        max_r = nums[n//2]
        for i in range(n//2+1,n):
            tmp += nums[i]
            max_r = max(max_r,tmp)
        return max(max_left,max_right,max_l+max_r)

总结

对于序列的dp问题,可定义状态方式:

  1. dp[i] 代表序列的前i个元素的情况
  2. dp[i] 以序列的第i个元素结尾的情况
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值