题目描述
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
解题方法
尝试1:暴力搜索,遍历所有可能的区间组合
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
dp = [[0 for j in range(len(nums))] for i in range(len(nums))]
for i in range(len(nums)):
dp[i][i] = nums[i]
max_sum = nums[0]
for i in range(len(nums)):
max_sum_i = dp[i][i]
for j in range(i+1,len(nums)):
dp[i][j] = dp[i][j-1] + nums[j]
max_sum_i = max(max_sum_i,dp[i][j])
max_sum = max(max_sum,max_sum_i)
return max_sum
时间复杂度 O ( n 2 ) O(n^2) O(n2)
尝试2:积分图避免重复求和计算,遍历所有可能区间找最大
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
dp = [[0 for j in range(len(nums))] for i in range(len(nums))]
dp = [0 for i in range(len(nums))]
dp[0] = nums[0]
for i in range(1,len(nums)):
dp[i] = dp[i-1] + nums[i]
dp = [0] + dp
max_sum = max(nums)
for i in range(len(nums)):
for j in range(i+1,len(nums)):
sum_ij = dp[j+1] - dp[i]
max_sum = max(max_sum,sum_ij)
return max_sum
时间复杂度 O ( n 2 ) O(n^2) O(n2)
一维dp
状态定义
dp[i]代表第i位结尾的子串的最大自序和
状态转移
dp[i] = max(dp[i-1]+nums[i],nums[i])
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
dp = [nums[0] for i in range(len(nums))]
for i in range(1,len(nums)):
dp[i] = max(dp[i-1]+nums[i],nums[i])
return max(dp)
思路点评
按照暴力搜索方法,需要遍历子串的首和尾 O ( n 2 ) O(n^2) O(n2)种组合。该方法非常巧妙,只遍历子串的末位置,因为存储了上一位置数字为结尾的最大自序和,接上当前位置数字后,最大子序和只能是上一位置结果+当前数字结果与当前数字中较大的一个。这样一来不关注子串的起始位置,很巧妙的将时间复杂度变为了O(n)。
贪心法
每一步中如果加上当前元素子串和变为负数,说明后续搜索中最大和子串一定不包含当前元素之前的部分。
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
max_num = nums[0]
sub_sum = 0
for i in range(len(nums)):
sub_sum += nums[i]
max_num = max(max_num, sub_sum)
if sub_sum < 0:
sub_sum = 0
return max_num
分治
将序列分出两半,最大子序和的子串存在位置如下情况:
- 左半区间
- 右半区间
- 横跨左半区间和右半区间
横跨两区间的最大子序和=从左半区间右端逆序遍历找到最大子序和+右半区间左端顺序遍历最大子序和
时间复杂度 O(nlogn)
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
n = len(nums)
if n <= 2:
return max(nums+[sum(nums)])
max_left = self.maxSubArray(nums[:n//2])
max_right = self.maxSubArray(nums[n//2:])
tmp = nums[n//2-1]
max_l = nums[n//2-1]
for i in range(n//2-2,-1,-1):
tmp += nums[i]
max_l = max(max_l,tmp)
tmp = nums[n//2]
max_r = nums[n//2]
for i in range(n//2+1,n):
tmp += nums[i]
max_r = max(max_r,tmp)
return max(max_left,max_right,max_l+max_r)
总结
对于序列的dp问题,可定义状态方式:
- dp[i] 代表序列的前i个元素的情况
- dp[i] 以序列的第i个元素结尾的情况