题目:https://leetcode-cn.com/problems/maximum-subarray/
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:
如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
解题思路:
动态规划普通解法见代码1,转移方程为dp[i]=max{dp[i-1]+nums[i], nums[i]} ,其中dp[i]为以nums[i]结尾的连续子数组的和。由于dp[i]的值只与dp[i-1]有关,所以还可以进一步优化,使用滚动数组思想。
“滚动数组”就是把数据暂存起来然后再利用,比如这道题dp[i]只与dp[i-1]有关,则可以用p记录dp[i-1],用q记录dp[i],此时for循环里的语句变为:
q = max{p+nums[i], nums[i]}
p = q
即每次更新p用于下次循环
简写可写做:
q = max{q+nums[i], nums[i]} (等号左边的是新q,等号右边的是上一循环的q)
若dp[i]的值只与dp[i-1]、dp[i-2]有关,举个例子:dp[i]=dp[i-1]+dp[i-2],用p记录dp[i-1], q记录dp[i-2],r记录dp[i],则for循环改为:
r = p + q
q = p
p = r
即用p去更新q、r去更新p用于下次循环(下一次循环中i+1,原i-1变为i,i-2变为i-1,所以用q=p,p=r,注意q=p在前,若p=r写在前面,q=p则更新成了r的值)
了解了滚动数组后再看这道题,需要对滚动数组进行改造,因为此题不是为了求dp[i],从代码1可以看出dp[i]求完后还需要遍历一遍dp数组求出最大数,所以需要在滚动时进行最大值的记录。
即for循环中可写做:
q = max{q+nums[i], nums[i]}
res = max(q, res)
即代码2所示
这时空间复杂度就降到了O(1)
代码1:
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
m = len(nums)
dp = [0]*m
dp[0] = nums[0]
if(m == 1):
return dp[0]
for i in range(1, m):
dp[i] = max(dp[i - 1]+nums[i], nums[i])
maxvalue = dp[0]
for i in range(1, m):
if(dp[i] > maxvalue):
maxvalue = dp[i]
return maxvalue
代码2:
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
res = nums[0]
cur = 0
for i in nums:
cur = max(cur + i, i)
res = max(cur, res)
return res
测试:
if __name__ == "__main__":
nums = [-2,1,-3,4,-1,2,1,-5,4]
res = Solution().maxSubArray(nums)
print(res)