LeetCode算法日记 - Day 81: 最大子数组和
目录
1. 最大子数组和
1.1 题目解析
1.2 解法
1.3 代码实现
1. 最大子数组和
https://leetcode.cn/problems/maximum-subarray/description/
给你一个整数数组 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
1.1 题目解析
题目本质
在一维数组中找到和最大的连续子序列,本质是"连续区间求和的最优选择"问题。
常规解法
枚举所有可能的子数组(双重循环确定起点和终点),计算每个子数组的和,取最大值。
问题分析
暴力枚举需要 O(n²) 时间复杂度(或 O(n³) 如果求和也用循环),当数组长度达到 10⁵ 时会超时。关键问题是存在大量重复计算——每次都从头累加子数组的和。
思路转折
要想高效 → 必须避免重复计算 → 动态规划。核心洞察:当遍历到位置 i 时,只需要决定"是接上前面的子数组"还是"从当前位置重新开始"。这样可以在 O(n) 时间内完成,因为每个位置只需要依赖前一个位置的状态。
1.2 解法
算法思想:定义 dp[i] 表示以 nums[i] 结尾的最大子数组和。
递推公式:dp[i] = max(dp[i-1] + nums[i], nums[i])
含义:要么延续前面的子数组(加上当前元素),要么放弃前面的(从当前元素重新开始)。
步骤拆解:
i)初始化 DP 数组,dp[0] 设为一个极小值作为哨兵
ii)遍历数组,对每个位置 i 计算 dp[i] = max(dp[i-1] + nums[i-1], nums[i-1])
iii)遍历 DP 数组找到最大值并返回
易错点:
-
初始化问题:dp[0] 和 cur 不能设为 0,因为数组可能全是负数。使用 Integer.MIN_VALUE/2 避免加法溢出
-
索引偏移:代码中 dp 数组长度为 m+1,dp[i] 对应 nums[i-1],需要注意下标映射
-
最终结果:不是返回 dp[m],而是返回整个 DP 数组中的最大值,因为最大子数组可能在任意位置结束
1.3 代码实现
class Solution {public int maxSubArray(int[] nums) {int m = nums.length;int[] dp = new int[m + 1];dp[0] = Integer.MIN_VALUE / 2; // 哨兵,避免溢出// 计算以每个位置结尾的最大子数组和for (int i = 1; i <= m; i++) {dp[i] = Math.max(dp[i-1] + nums[i-1], nums[i-1]);}// 找出所有位置中的最大值int cur = Integer.MIN_VALUE / 2;for (int i = 1; i <= m; i++) {cur = Math.max(cur, dp[i]);}return cur;}
}
复杂度分析
-
时间复杂度:O(n),两次遍历数组
-
空间复杂度:O(n),使用了 DP 数组。可优化至 O(1),因为每次只需要前一个状态
