题目地址(53. 最大子数组和)
https://leetcode-cn.com/problems/maximum-subarray/
题目描述
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入:nums = [1]
输出:1
示例 3:
输入:nums = [5,4,-1,7,8]
输出:23
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的 分治法 求解。
前置知识
- 滑动窗口
- 动态规划
公司
- 阿里
- 百度
- 字节
- 腾讯
- bloomberg
- microsoft
思路
关键点
-
解法一:
- 使用分治法,将任务分解,从两个角度来观察用递归实现的分治法。
-
从上到下(由大到小):
我们把数组nums以中间位置(m)分为左(left)右(right)两部分. 那么有, left = nums[0]…nums[m - 1] 和 right = nums[m + 1]…nums[n-1]
最大子序列和的位置有以下三种情况:- 考虑中间元素nums[m], 跨越左右两部分,这里从中间元素开始,往左求出后缀最大,往右求出前缀最大, 保持连续性。
- 不考虑中间元素,最大子序列和出现在左半部分,递归求解左边部分最大子序列和
- 不考虑中间元素,最大子序列和出现在右半部分,递归求解右边部分最大子序列和
分别求出三种情况下最大子序列和,三者中最大值即为最大子序列和。
-
从小到大:
从上往下(大数组如何划分为小数组):左边数组 中间值 右边数组 中间值合并前缀和、后缀和结果 -inf 中间值 -inf 中间值 -inf 第一个值 第二个值 max(中间值,中间值+第二个值) 第一个元素 中间值 第三个元素 max(中间值,中间值+第一个值,中间值+第三个值)
该解法每次将数组分为三部分,一次计算中间值+前后缀和(两个循环实现),一次计算左边最大子序列,一次计算右边子序列。
递归的判断边界:至少处理一个节点;如果l>r,返回-inf。
递归划分条件:helper(l,mid-1) ; helpder(mid+1,r)
-
解法二:
动态规划的难点在于找到状态转移方程,
dp[i] - 表示到当前位置 i 的最大子序列和
状态转移方程为:dp[i] = max(dp[i - 1] + nums[i], nums[i])
初始化:dp[0] = nums[0]
从状态转移方程中,我们只关注前一个状态的值,所以不需要开一个数组记录位置所有子序列和,只需要两个变量,
currMaxSum - 累计最大和到当前位置i
maxSum - 全局最大子序列和:
currMaxSum = max(currMaxSum + nums[i], nums[i])
maxSum = max(currMaxSum, maxSum)
代码
- 语言支持:Python3
Python3 Code:
解法一:
# import sys
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
return self.helper(nums, 0, len(nums)-1)
def helper(self, nums, l, r):
if l>r:
return -sys.maxsize
mid = (l+r) // 2
left = self.helper(nums, l, mid-1)
right = self.helper(nums, mid+1, r)
# 默认左边后缀和以及右边前缀和都为0
left_suffix_max_sum = right_prefix_max_sum = 0
sum = 0
for i in reversed(range(l,mid)):
sum += nums[i]
left_suffix_max_sum = max(left_suffix_max_sum, sum)
sum = 0
for i in range(mid+1, r+1):
sum += nums[i]
right_prefix_max_sum = max(right_prefix_max_sum, sum)
cross_max_sum = left_suffix_max_sum + right_prefix_max_sum + nums[mid]
return max(cross_max_sum,left, right)
复杂度分析
令 n 为数组长度。
- 时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
- 空间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
解法二:
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
n = len(nums)
max_sum_ending_curr_index = max_sum = nums[0]
for i in range(1,n):
max_sum_ending_curr_index = max(max_sum_ending_curr_index+nums[i],nums[i])
max_sum = max(max_sum_ending_curr_index, max_sum)
return max_sum
复杂度分析
令 n 为数组长度。
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)