LeetCode 53 最大子数组和:一次遍历掌握 Kadane 算法

前言

“最大子数组和”是数组与动态规划中的经典题目。它的代码只有几行,但真正重要的是理解:当我们遍历到一个新元素时,什么时候应该延续前面的子数组,什么时候应该从当前元素重新开始。

这道题对应 LeetCode 53,核心解法通常称为 Kadane 算法。它既可以从动态规划角度理解,也可以看作一种基于局部最优决策的贪心过程。

一、题目说明

给定一个整数数组 nums,找出一个具有最大和的连续子数组,并返回该子数组的元素和。子数组至少包含一个元素。

例如:

输入:nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
输出:6
解释:连续子数组 [4, -1, 2, 1] 的和最大,为 6。

这里最重要的限制是“连续”。我们不能像子序列问题一样跳过中间元素。

二、暴力枚举为什么不够好?

最直观的办法是枚举所有子数组的起点和终点,再计算每个区间的和。如果使用前缀和优化区间求和,仍然需要枚举两个端点,时间复杂度为 O(n²);如果每次重新累加,复杂度甚至可能达到 O(n³)

实际上,我们没有必要反复计算每个区间。遍历到当前元素时,只需要知道一个信息:以前一个位置结尾的最大子数组和,是否值得继续保留?

三、Kadane 算法的核心思想

定义两个变量:

  • current:以当前元素结尾的最大子数组和;

  • result:遍历过程中出现过的全局最大子数组和。

对于当前元素 nums[i],只有两种选择:

  1. 接在前面的子数组后面,得到 current + nums[i]

  2. 放弃前面的子数组,从当前元素重新开始,得到 nums[i]

因此状态转移公式为:

current = max(nums[i], current + nums[i])
result  = max(result, current)

为什么前面的和小于零时可以直接放弃?假设此前的子数组和为 -3,当前元素为 4

-3 + 4 = 1

显然不如直接从 4 开始。任何负数前缀都会拖累后面的结果,所以它不可能成为最优子数组的一部分。

反过来,如果此前的和为正数,它能够增大当前元素构成的子数组和,就应该继续保留。这个判断就是 Kadane 算法能够一次遍历完成计算的关键。

四、示例演算

以数组 [-2, 1, -3, 4, -1, 2, 1, -5, 4] 为例:

当前元素current:以当前位置结尾的最大和result:全局最大和
-2-2-2
111
-3-21
444
-134
255
166
-516
456

遍历到 4 时,前面的 current-2,继续累加只会拖累结果,因此从 4 重新开始。随后依次加入 -1、2、1,最终得到最大和 6

注意,遍历到 -5 时,current 虽然下降为 1,但仍然是正数,所以理论上仍有保留价值。最终加入 4 得到 5,没有超过全局最大值 6

五、Java 代码实现

1. 标准动态规划写法

class Solution {
    public int maxSubArray(int[] nums) {
        int current = nums[0];
        int result = nums[0];

        for (int i = 1; i < nums.length; i++) {
            current = Math.max(nums[i], current + nums[i]);
            result = Math.max(result, current);
        }

        return result;
    }
}

这段代码直接对应状态转移公式,含义也最清晰:当前元素要么加入原子数组,要么成为新子数组的起点。

2. 等价写法

class Solution {
    public int maxSubArray(int[] nums) {
        int result = nums[0];
        int sum = 0;

        for (int num : nums) {
            if (sum > 0) {
                sum += num;
            } else {
                sum = num;
            }
            result = Math.max(result, sum);
        }

        return result;
    }
}

两种写法本质完全相同:

sum > 0  → 延续原子数组
sum <= 0 → 从当前元素重新开始

标准写法更方便推导动态规划公式;截图写法则更加直观地体现了“负数前缀立即舍弃”的思想。

六、复杂度分析

  • 时间复杂度:O(n)。只需要遍历数组一次;

  • 空间复杂度:O(1)。只使用两个变量,没有额外数组。

如果使用完整的动态规划数组,令 dp[i] 表示以 nums[i] 结尾的最大子数组和,也可以求解,但每个状态只依赖前一个状态,因此可将空间从 O(n) 优化为 O(1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值