678 字
3 分钟
53. 最大子数组和

53. 最大子数组和#

题目:https://leetcode.cn/problems/maximum-subarray/

思路:#

原始思路:最初这个子数组为 nums 中所有元素,然后如果左边的缩减能让子数组变大,那么就缩减;右边同理。

这样的考虑是欠缺的,因为不知道哪个数会被包含其中,且边界问题无法解决。

正确思路:

  • 这道题要求的是连续,而且题目只要求返回结果,而不是求最大连续子数组是哪一个。由以上条件可以推出本道题通常可以使用动态规划解决。
  • 题目要求出最大和的连续子数组,但不知道是包含哪个数的最大和的连续子数组,那么我们只要求出对于每一个数的最大和的连续子数组就可以了。
  • 但当前设定存在有后效性,意思就是说,不确定这个数属于连续子数组的第几个元素。于是,我们重新定义,改为:以每一个数结尾的最大和的连续子数组
  • 观察第 i 个子问题和第 i+1 个子问题,可以发现:如果以 i 结尾的最大连续子数组小于等于 0,那么以 i+1 结尾的最大连续子数组等于第 i 个值
  • 于是本道动态规划题定义如下:
    • 定义状态:dp[i]:表示以 nums[i] 结尾的连续子数组的最大值。
    • 状态转移方程:
      • 如果 dp[i - 1] > 0,那么可以把 nums[i] 直接接在 dp[i - 1] 表示的那个数组的后面,得到和更大的连续子数组;
      • 如果 dp[i - 1] <= 0,那么 nums[i] 加上前面的数 dp[i - 1] 以后值不会变大。于是 dp[i] 「另起炉灶」,此时单独的一个 nums[i] 的值,就是 dp[i]。
    • 结果:结果为 dp 数组中的最大值

优化#

因为最后要求的是最大值,那么状态转移方程可以是:dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);

其次,结果中求最大值的过程可以在状态转移中求出

最后,第 i 次的状态只和 i - 1 次有关,所以可以节省一点空间。

代码#

动态规划#

基础版

class Solution {
    public int maxSubArray(int[] nums) {
        int len = nums.length;

        int[] dp = new int[len];
        dp[0] = nums[0];

        for (int i = 1; i < len; i++) {
            if (dp[i - 1] > 0) {
                dp[i] = dp[i - 1] + nums[i];
            } else {
                dp[i] = nums[i];
            }
        }

        int res = dp[0];
        for (int i = 1; i <  len; i++) {
            res = Math.max(res, dp[i]);
        }
        return res;
    }
}

优化版

public class Solution {

    public int maxSubArray(int[] nums) {
        int pre = 0;
        int res = nums[0];
        for (int num : nums) {
            pre = Math.max(pre + num, num);
            res = Math.max(res, pre);
        }
        return res;
    }
}
53. 最大子数组和
https://blog.hzau.top/posts/algorithm/53-maximum-subarray/
作者
Dawn Journey
发布于
2025-04-23
许可协议
CC BY-NC-SA 4.0