1、最大子序和(53)
题目描述:
【简单题】
题目链接
思路分析
题解一:分治方法
【分解】连续子序列的最大和主要可由这三部分子区间里最大子序和的最大值得到,因此,可将原问题分解为在以下三个子区间分别求最大子序和的问题。当然,这些三个区间又可以以这样的方式不断的划分,直到子子区间的长度为1.
- 第 1 部分:左子区间 [left, mid]
- 第 2 部分:右子区间 [mid + 1, right]
- 第 3 部分:包含/跨越子区间 [mid , mid + 1] 的子区间,即 nums[mid] 与 nums[mid + 1] 一定会被选取
【解决】
- 主要的还是求解跨越左右区间划分点的中间区域的最大子序和:此时需计算左子区间中从右端点开始(包含右端点)从右向左的最大子序和和右子区间中从最左端点开始(包含左端点)从左向右的最大子序和,再将这两个最大子序和相加。
- 递归求解左子区间的最大子序和
- 递归求解右子区间的最大子序和
【合并】
- 返回三大区间解的最大值。
【代码实现】
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
n = len(nums)
#递归终止条件
if n == 1:
return nums[0]
else:
#递归计算左半边最大子序和
max_left = self.maxSubArray(nums[0:len(nums) // 2])
#递归计算右半边最大子序和
max_right = self.maxSubArray(nums[len(nums) // 2:len(nums)])
#关建一步:计算中间的最大子序和:从右到左计算左边的最大子序和,从左到右计算右边的最大子序和,然后两值再相加。
max_l = nums[len(nums) // 2 - 1]
tmp = 0
for i in range(len(nums) // 2 - 1, -1, -1):
tmp += nums[i]
max_l = max(tmp, max_l)
max_r = nums[len(nums) // 2]
tmp = 0
for i in range(len(nums) // 2, len(nums)):
tmp += nums[i]
max_r = max(tmp, max_r)
#返回三个中的最大值
return max(max_right,max_left,max_l+max_r)
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度:递归会使用 O ( log n ) O(\log n) O(logn) 的栈空间,故渐进空间复杂度为 O ( log n ) O(\log n) O(logn)。
题解二:动态规划
第1步:定义状态
既然一个连续子数组一定要以一个数作为结尾,那么我们可以将状态定义成如下:
dp[i]
:表示以num[i]
结尾的连续子数组的最大和
第2步:状态转移方程
根据状态的定义,由于 nums[i]
一定会被选取,并且 dp[i]
所表示的连续子序列与 dp[i - 1]
所表示的连续子序列(有可能)就差一个 nums[i]
。
假设数组 nums
全是正数,那么一定有 dp[i] = dp[i - 1] + nums[i]
,但是搞不好 dp[i - 1]
是负数也是有可能的。例如前几个数都是负数,突然来了一个正数。
于是分类讨论:
-
如果
dp[i - 1]>= 0