题目描述:
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
题目链接:53.最大子序和
【解法一】:动态规划:
设
f
[
i
]
f[i]
f[i]为以第i个元素结尾的最大的连续子数组的和。假设对于元素i,所有以它前面的元素结尾的子数组的长度都已经求得,那么以第i个元素结尾且和最大的连续子数组实际上,要么是以第i-1个元素结尾且和最大的连续子数组加上这个元素,要么是只包含第i个元素,
即
f
[
i
]
=
m
a
x
(
f
[
i
−
1
]
+
n
u
m
[
i
]
,
n
u
m
[
i
]
)
f[i]= max(f[i-1] + num[i], num[i])
f[i]=max(f[i−1]+num[i],num[i])。可以通过判断
f
[
i
−
1
]
+
n
u
m
[
i
]
f[i-1] + num[i]
f[i−1]+num[i]是否大于
n
u
m
[
i
]
num[i]
num[i]来做选择,而这实际上等价于判断f[i-1]是否大于0。由于每次运算只需要前一次的结果,因此并不需要像普通的动态规划那样保留之前所有的计算结果,只需要保留上一次的即可,因此算法的时间和空间复杂度都很小
AC代码【Python】:
# 动态规划
def maxSubArray(self, nums: List[int]) -> int:
if not nums:
return 0
pre = 0
maxAns = nums[0]
for i in nums:
pre = max(pre + i, i)
maxAns = max(maxAns, pre)
return maxAns
【解法二】:分治法:
分治法,最大子序和要么在左半部分,要么在右半部分,要么就横跨两部分(即包括左半部分的最后一个元素,和右半部分的第一个元素)。返回这三种情况的最大值即可。第三种情况,其中包括左半部分最后一个元素的情形,需要挨个往前遍历,更新最大值。包含右半部分的第一个元素的情况类似。总的时间复杂度O(nlogn)
AC代码【Python】:
# 分治法
def maxSubArray(self, nums: List[int]) -> int:
# 主函数
left = 0
# 左右边界
right = len(nums) - 1
# 求最大和
maxSum = self.divide(nums, left, right)
return maxSum
def divide(self, nums, left, right):
# 如果只有一个元素就返回
if left == right:
return nums[left]
# 确立中心点
center = (left + right) // 2
# 求子序在中心点左边的和
leftMaxSum = self.divide(nums, left, center)
# 求子序在中心点右边的和
rightMaxSum = self.divide(nums, center + 1, right)
# 求子序横跨2边的和,分成左边界和和右边界和
leftBorderSum = nums[center]
leftSum = nums[center]
for i in range(center - 1, left - 1, -1):
leftSum += nums[i]
if leftSum > leftBorderSum:
# 不断更新左区块的最大值
leftBorderSum = leftSum
rightBorderSum = nums[center + 1]
rightSum = nums[center + 1]
for i in range(center + 2, right + 1):
rightSum += nums[i]
if rightSum > rightBorderSum:
# 不断更新右区块的最大值
rightBorderSum = rightSum
# 左边界的和 + 右边那块的和
BorderSum = leftBorderSum + rightBorderSum
return max(leftMaxSum, rightMaxSum, BorderSum)
虽然这道题在Leetcode中标记为简单题,但是此题的dp和分治解法值得学习
参考: