力扣hot100之最大子数组和(Java版)

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

2、解法

方法一:暴力

bash 复制代码
import java.util.ArrayList;
import java.util.Collections; // 需要导入 Collections 来使用 max 方法

class Solution {
    public int maxSubArray(int[] nums) {
        ArrayList<Integer> result = new ArrayList<>();

        // 外层循环:确定子数组的起始位置
        for (int i = 0; i < nums.length; i++) {
            int sum = 0; // 每次换起始位置时,重置和

            // 内层循环:确定子数组的结束位置,并累加求和
            for (int j = i; j < nums.length; j++) {
                sum += nums[j];       // 累加当前元素
                result.add(sum);      // 每累加一次,就得到了一个以 i 开头、j 结尾的子数组的和,存入列表
            }
        }

        //使用 Collections.max() 来获取列表中的最大值
        return Collections.max(result);
    }
}

这种方法虽然可以,但是时间复杂度为O(n^2),当数据非常大的时候,会超出内存限制。

方法二:进阶版

虽然上面的代码可以通过部分测试,但使用 ArrayList 存储所有的和会消耗大量内存(空间复杂度较高)。其实不需要把所有和都存下来,只需要用一个变量 max 记录当前遇到的最大值即可。

bash 复制代码
class Solution {
    public int maxSubArray(int[] nums) {
        // 初始化最大值为整数最小值,防止数组全是负数时出错
        int max = Integer.MIN_VALUE;

        for (int i = 0; i < nums.length; i++) {
            int sum = 0;
            for (int j = i; j < nums.length; j++) {
                sum += nums[j];
                // 每次算出新和,直接比较并更新最大值
                if (sum > max) {
                    max = sum;
                }
            }
        }
        return max;
    }
}

这种呢,也能通过,虽然比上面第一种方法通过的多一点,但是不能全部通过案例

方法三:动态规划

动态规划是一种通过将复杂问题分解为更简单的子问题,并存储这些子问题的解以避免重复计算,从而高效求解最优化问题的算法设计方法。 ------ 源自《算法导论》(Introduction to Algorithms, CLRS)

其核心基于两个数学性质:

  1. 最优子结构(Optimal Substructure) 问题的最优解包含其子问题的最优解。

  2. 重叠子问题(Overlapping Subproblems) 在递归求解过程中,相同的子问题被多次重复计算。


动态规划设计的核心知识点(标准步骤):

1. 定义状态(State)
  • 用一个变量或数组 dp[i]dp[i][j] 等表示子问题的解

  • 例如:dp[i] 表示"前 i 个元素中能获得的最大和"。

2. 确定状态转移方程(Recurrence Relation)
  • 描述当前状态如何由之前的状态推导而来。

  • 例如:dp[i] = max(dp[i-1] + nums[i], nums[i])(最大子数组和)

3. 设定初始条件(Base Case)
  • 最简单子问题的解,作为递推起点。

  • 例如:dp[0] = nums[0]

4. 确定计算顺序(Order of Computation)
  • 通常自底向上(Bottom-up):从小到大填表(如 for 循环)

  • 也可自顶向下(Top-down):递归 + 记忆化(Memoization)

5. 空间优化(可选)
  • 若当前状态只依赖前几个状态,可用滚动变量代替整个数组。

  • 例如:斐波那契数列只需 a, b, c 三个变量。


动态规划的两种实现方式:

方式 描述 特点
自底向上(迭代) 从最小的子问题开始,逐步构建到原问题 无递归开销,效率高,常用
自顶向下(递归+记忆化) 从原问题出发,递归分解,用哈希表/数组缓存结果 思路直观,但有函数调用开销

经典 DP 问题类型(体现设计思想):

  • 线性 DP:最大子数组和、打家劫舍

  • 区间 DP:最长回文子串、矩阵链乘

  • 背包 DP:0-1 背包、完全背包

  • 树形 DP:二叉树最大路径和

  • 状态机 DP:股票买卖问题


关键总结(一句话):

动态规划 = 最优子结构 + 重叠子问题 + 状态定义 + 状态转移 + 初始条件 + 计算顺序

简单理解一下

动态规划(Dynamic Programming,简称 DP)是一种解决复杂问题的聪明办法把大问题拆成小问题,先解决小问题,再用小问题的答案一步步推出大问题的答案


核心思想(3句话):

  1. 分阶段:把问题分成一步步(比如从第1天到第n天)。

  2. 记答案:把每一步的结果存下来(避免重复算)。

  3. 推着走:后面的答案 = 前面的答案 + 当前选择。


举个简单例子:

爬楼梯:每次能爬 1 或 2 阶,问到第 5 阶有几种方法?

  • 到第1阶:1种

  • 到第2阶:2种(1+1 或 直接2)

  • 到第3阶:= 到第2阶的方法 + 到第1阶的方法 = 2 + 1 = 3

  • 到第4阶:= 第3阶 + 第2阶 = 3 + 2 = 5

  • 到第5阶:= 第4阶 + 第3阶 = 5 + 3 = 8

每一步都用前面的结果,不用从头算!


动态规划的两个条件:

  1. 有重复的小问题(比如算第5阶时,第3阶被用了好多次)

  2. 大问题的最优解 = 小问题的最优解组合起来


一句话总结:

动态规划 = 记住已经算过的,别重复干傻事,从小问题一步步推出大答案

这是解决此问题的标准解法。核心思想是:如果前面的子数组和变成了负数,那就果断丢弃,从当前元素重新开始计算。

  • 思路:
    • 我们维护两个变量:
      • current_sum:以当前元素结尾的连续子数组的最大和。
      • max_sum:全局最大的子数组和。
    • 对于数组中的每一个数 num,我们需要决定是将它加入之前的子数组,还是把它作为新子数组的开头。
      • 如果 current_sum 是负数,加上它只会让结果更小,不如直接从 num 开始。
      • 如果 current_sum 是正数,加上 num 可能会更大,那就加上。
bash 复制代码
class Solution {
    public int maxSubArray(int[] nums) {
        // 初始化:假设第一个元素就是最大值
        int current_sum = nums[0];
        int max_sum = nums[0];

        // 从第二个元素开始遍历
        for (int i = 1; i < nums.length; i++) {
            // 核心逻辑:
            // 如果 current_sum < 0,抛弃它,从 nums[i] 重新开始
            // 如果 current_sum >= 0,保留它,加上 nums[i]
            current_sum = Math.max(nums[i], current_sum + nums[i]);
            
            // 更新全局最大值
            max_sum = Math.max(max_sum, current_sum);
        }
        
        return max_sum;
    }
}
相关推荐
JieE2124 小时前
LeetCode 101. 对称二叉树|JS 递归 + 迭代双解法,彻底搞懂镜像判断
javascript·算法
JieE2121 天前
LeetCode 56. 合并区间|超清晰 JS 图解思路,面试高频区间题
javascript·算法·面试
Jack201 天前
HarmonyOS开发中错误处理策略:网络异常统一处理
算法
小小杨树1 天前
读懂色彩:拍照调色不再难
算法·计算机视觉·配色
JieE2122 天前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE2122 天前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
vivo互联网技术2 天前
CVPR 2026 | 全新强化学习框架 BeautyGRPO:重塑真实人像
算法·大模型·cvpr·影像
Darling噜啦啦2 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
用户497863050733 天前
(一)小红的数组操作
算法·编程语言