本题多解,非常巧妙!都了解一下!

Leetcode Link: 53. 最大子数组和 - 力扣(LeetCode) 剑指 Offer 42. 连续子数组的最大和 - 力扣(LeetCode)

题目

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

子数组 是数组中的一个连续部分。

解法一

思路:自己的想法

譬如说对于例 1 序列 [-2,1,-3,4,-1,2,1,-5,4],我们知道它的答案是 [4,-1,2,1] 的和,难点在于什么时候应该新开始计算一个子序列的和,抛弃之前累加的子序列的和?

显然,新来一个数字 val,我们在考虑是不是把他加入到当前子序列中,需要它对这个和有贡献。但是这样想就有问题,显然答案中 -1 对和没有贡献,但是后面的贡献能够消除 -1 的影响,所以不能扔掉 -1,那应该怎么办呢?

我们从新来的 val 来考虑,对于这个 val,什么时候他不应该算上一个子序列里?显然,如果当前这个 val 加上之前的子序列比自己都要小,那么我 val 为什么要自贬身价,和之前的子序列合并呢?而是应该算作一个新的子序列的开始!

那么怎么找到子序列的最大和呢?显然这个和是一直变化的。到目前我没有想到最好的一步到位的方法,可以在每一步都算一下max(res, tmp_sum),不过可以确定的是,当新开始子序列的时候,当前的res一定会是val

题解

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        tmp_sum = nums[0] # 题目中输入长度一定 > 0
        res = tmp_sum
        for val in nums[1:]:
	        # "我"+前面的子序列和比"我"还小,所以"我"自立门户
            if val + tmp_sum <= val:
                tmp_sum = val
                res = max(res, val)
            else:
                tmp_sum += val
                res = max(res, tmp_sum)
        return res
 

解法二

随想录的思路,贪心在和为负数的负数的时候直接抛弃,和我的思路一样,不过我的可能稍微好想一点?

和解法 1 的区别 解法一:思考当前这个要还是开辟一个新的 解法二:思考当前的这个和要还是用新的。

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        cur_sum = 0
        res = -float('inf') 
        p = 0
        while(p < len(nums)):
            if cur_sum < 0:
                cur_sum = nums[p]
            else:
                cur_sum += nums[p]
            p += 1
            res = max(res, cur_sum)
        return res

解法三

思路:动态规划

dp 五步走

  1. 确定下标和内容的意义:dp[i]:包括下标 i 之前的最大连续子序列和为 dp[i]
  2. 递推公式:由前一个位置 dp[i-1]+nums[i]推出,如果比自己还小,则是自己 nums[i],即 dp[i] = max (dp[i-1]+nums[i], nums[i])
  3. dp 数组初始化:dp[0] = nums[0]
  4. 遍历顺序:正向
  5. 举例推导 dp 数组

题解

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

启发和联系