【hot100】数组--53最大子数组和

97 阅读2分钟

1. 题目描述

给你一个整数数组 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

2. 解题思路

(1)贪心算法

在数组中,找出最大和的连续子数组。可以理解为查找局部最优解。那么此时可以想到使用贪心算法来实现。

局部最优解:当前“连续和”为负数时,立即放弃,从下一个元素重新计算。因为负数加上下一个元素,“连续和”只会越来越小。

全局最优:选取最大“连续和”

从代码角度上来讲:遍历 nums,从头开始 sum 累积,如果 sum 一旦加上 nums[i] 变为负数,那么就应该从 nums[i+1] 开始从 0 累积 sum,因为已经变为负数的 count,只会拖累总和。

这相当于是暴力解法中的不断调整最大子序和区间的起始位置。

image.png

(2)动态规划

由于当前最大子序列和只和前面的子序列和相关,因此也可以考虑使用动态规划的方式来进行。

  1. 确定 dp 数组及下标的含义:
  • dp[i]:包括下标的最大连续子序列和为 dp[i]
  1. 确定递推公式

dp[i] 只有两个方向可以推出来:

  • dp[i-1]+nums[i],即:nums[i] 加入当前连续子序列和
  • nums[i],即:从头开始计算当前连续子序列和

一定取最大的,所以 dp[i]=max(dp[i-1]+nums[i],nums[i])

  1. dp 数组初始化

由递推公式可以看出,dp[i] 是依赖于 dp[i-1] 的状态,因此 dp[0] 就是递推公式的基础。dp[0]=nums[0]

  1. 确定遍历顺序

递推公式中 dp[i] 依赖于 dp[i-1] 状态,需要从前往后遍历。

  1. 举例推导 dp 数组

以示例一为例,输入:nums = [-2,1,-3,4,-1,2,1,-5,4],对应的dp状态如下:

image.png

3. 代码实现

贪心算法实现:

var maxSubArray = function(nums) {
  let max=-Infinity;
  let sum=0;
  for(let i=0;i<nums.length;i++){
    sum+=nums[i]
    if(sum>max){
      max=sum
    }
    if(sum<0){
      sum=0
    }
  }
  return max
};
  • 时间复杂度:O(n),只进行一层遍历
  • 空间复杂度:O(1)

动态规划实现:

var maxSubArray=function(nums){
  const len=nums.length
  let dp=new Array(len+1).fill(0)
  dp[0]=nums[0]
  let max=dp[0]
  for(let i=0;i<len;i++){
    let dp[i]=Math.max(dp[i-1]+nums[i],nums[i])
    max=Math.max(max,dp[i])
  }
  return max
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)