给你一个整数数组
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)
的解法,尝试使用更为精妙的 分治法 求解。
思路:
- 动态规划 方式1:
判断 dp[i] = dp[i-1] + nums[i] 中,dp[i-1] 是起到 正向作用还是负向作用
判断 nums[i] 是否要加上之前的 dp[i-1],有两种情况:- 当 dp[i-1] > 0 起正向作用时,则可以加上之前的dp[i-1];
- 反之 当 dp[i-1] < 0 起负向作用时,则不加
- 动态规划 方式2(方式1的优化版):
其实上述方式并不是最优的,因为每次遍历我只需要判断之前的 dp[i-1] 的正负即可,所以可以用一个变量 pre 来代替和复用,并不需要额外构造dp数组。这样空间复杂度可降至O(1)
时间复杂度:O(N)
空间复杂度:方式1 O(N),方式2 O(1)
// 动态规划-1 【空间复杂度O(n) 时间复杂度O(n)】
// 思路:之前的题,是已经选好了dp[i-1],然后来考虑要不要选第i个值。这道题是一定要选第i个值,然后再考虑要不要选第dp[i-1]。
func maxSubArray(nums []int) int {
n := len(nums)
dp := make([]int, n)
dp[0] = nums[0]
res := nums[0]
for i := 1; i < n; i++ {
// 判断 nums[i] 是否要加上之前的dp[i-1],有两种情况:
// 当 dp[i-1] > 0 起正向作用时,则可以加上之前的dp[i-1];反之 当 dp[i-1] < 0 起负向作用时,则不加
dp[i] = max(dp[i - 1] + nums[i], nums[i])
res = max(res, dp[i])
}
return res
}
// 动态规划-2 【上面代码的优化版 → 空间复杂度O(1) 时间复杂度O(n)】
// 每次遍历我只需要判断之前的dp[i-1]的正负即可,所以可以用一个变量preSum来复用
// 1、判断 nums[i]是否要加上之前的前缀和 preSum,有两种情况:
// 2、当 preSum > 0 对最大子数组和起正向作用时,则可以加上之前的preSum;
// 3、反之,当 preSum < 0 对最大子数组和起负向作用时,则不加
func maxSubArray(nums []int) int {
res, preSum := nums[0], 0
for i := 0; i < len(nums); i++ {
preSum = max(preSum + nums[i], nums[i])
res = max(res, preSum)
}
return res
}
func max(a, b int) int {
if a > b {
return a
}
return b
}