vlambda博客
学习文章列表

最大子序和问题leetcode53、leetcode918

leetcode53:

题目:

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

示例:

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

题解:本题的解决思路采用动态规划的思想,从第0个元素开始,每次保存到当前位置的最大子序和,存储到数据DP中,DP中最大的数即为数组nums的最大子序和。

代码:

class Solution: def maxSubArray(self, nums: List[int]) -> int: if not nums: return 0 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)


leetcode 918(与53相比就是有环和无环的区别):

题目:

        给定一个由整数数组 A 表示的环形数组 C,求 C 的非空子数组的最大可能和。

        在此处,环形数组意味着数组的末端将会与开头相连呈环状。(形式上,当0 <= i< A.length 时 C[i] = A[i],且当 i >= 0 时 C[i+A.length] =C[i])

        此外,子数组最多只能包含固定缓冲区 A 中的每个元素一次。(形式上,对于子数组 C[i], C[i+1], ..., C[j],不存在 i<= k1, k2 <= j 其中 k1 % A.length =k2 % A.length)

示例:

输入:[5,-3,5]
输出:10

解释:从子数组 [5,5] 得到最大和 5 + 5 = 10

题解:

分为以下两种情况

(1)最大值在中间部分,如图1所示,则和上题(leetcode 53)一样求解

图1

(2)最大在两端的情况,如图2所示,则中间部分则为最小子序和问题(为题leetcode 53的对偶问题),利用数组的总和减去最小子序和就可以得到最大子序和,但是当数组中的数全为负数是,数组总和和最小子序和相等,相减则为0,此时,应该取情况1的结果。

                                                    图2

代码:

class Solution: def maxSubarraySumCircular(self, nums: List[int]) -> int: length = len(nums) nums_sum, DP_max, DP_min = sum(nums), [0]*length, [0]*length DP_max[0], DP_min[0] = nums[0], nums[0] for i in range(1, length): DP_max[i] = max(nums[i], DP_max[i-1]+nums[i]) DP_min[i] = min(nums[i], DP_min[i-1]+nums[i])
if nums_sum == min(DP_min): #所有的数都是负数的情况 return max(DP_max) else: return max(max(DP_max), nums_sum-min(DP_min))