(13)LeetCode 53. 最大子数组和

LeetCode 53. 最大子数组和

题目描述

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


解法一:动态规划(Kadane 算法)⭐ 推荐

思路

经典的 Kadane 算法,利用动态规划思想,一次遍历即可求解。

定义 last 表示 以当前元素结尾的最大子数组和

对于每个元素 nums[i],要么将其接到之前的最优子数组后面(last + nums[i]),要么从当前位置重新开始(nums[i])。

即:

复制代码
last = max(nums[i], last + nums[i])

代码中简化为 last = nums[i] + max(last, 0),当 last < 0 时相当于放弃之前的部分。

同时用 res 记录所有 last 中的最大值,即为答案。

代码实现

cpp 复制代码
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int res = INT_MIN, last = 0;
        for (int i = 0; i < nums.size(); i++) {
            last = nums[i] + max(last, 0);
            res = max(res, last);
        }
        return res;
    }
};

复杂度分析

  • 时间复杂度:O(n),仅需一次遍历。
  • 空间复杂度:O(1),只使用常数个变量。

解法二:分治 / 线段树(更通用的思路)

思路

虽然 Kadane 足以解决本题,但分治法(线段树思想)更具扩展性,可轻松应对区间查询、单点修改等复杂问题。

我们定义一个结构体 Node,保存一个区间的四个关键信息:

  • sum:区间总和
  • s:区间内最大子数组和(允许为空,即和为 0)
  • ls:区间最大前缀和(从区间左端点开始,允许为空)
  • rs:区间最大后缀和(到区间右端点结束,允许为空)

然后递归分治合并左右子区间的信息,合并公式如下:

  • res.sum = L.sum + R.sum
  • res.s = max({L.s, R.s, L.rs + R.ls})
    最大子数组可能完全在左、完全在右,或跨越左右(左后缀 + 右前缀)
  • res.ls = max(L.ls, L.sum + R.ls)
    最大前缀要么在左区间内,要么包含整个左区间加上右区间的最大前缀
  • res.rs = max(R.rs, L.rs + R.sum)
    最大后缀要么在右区间内,要么包含左区间的最大后缀加上整个右区间

⚠️ 注意 :由于我们将空子数组视为和为 0,所以当数组全为负数时,build 会返回 0,但题目要求非空子数组,因此需要特判全负数的情况,直接返回最大负数。

代码实现

cpp 复制代码
class Solution {
public:
    struct Node {
        int sum;   // 区间总和
        int s;     // 区间最大子数组和(允许为空)
        int ls;    // 区间最大前缀和(允许为空)
        int rs;    // 区间最大后缀和(允许为空)
    };

    Node build(vector<int>& nums, int l, int r) {
        if (l == r) {
            int v = max(nums[l], 0);
            return {nums[l], v, v, v};
        }
        int mid = l + r >> 1;
        auto L = build(nums, l, mid);
        auto R = build(nums, mid + 1, r);
        Node res;
        res.sum = L.sum + R.sum;
        res.s = max(max(L.s, R.s), L.rs + R.ls);
        res.ls = max(L.ls, L.sum + R.ls);
        res.rs = max(R.rs, L.rs + R.sum);
        return res;
    }

    int maxSubArray(vector<int>& nums) {
        int res = INT_MIN;
        for (int x : nums) res = max(res, x);
        if (res < 0) return res;   // 全为负数,直接返回最大负数
        auto t = build(nums, 0, nums.size() - 1);
        return t.s;
    }
};

复杂度分析

  • 时间复杂度:O(n),递归遍历每个元素一次,合并操作 O(1)。
  • 空间复杂度:O(log n),递归栈深度(树高)。

两种解法对比

方法 时间复杂度 空间复杂度 适用场景
Kadane 动态规划 O(n) O(1) 仅需整个数组的最大子数组和,最简洁高效
分治 / 线段树 O(n) O(log n) 更通用,支持区间查询、动态修改等扩展需求

对于本题,Kadane 算法足以轻松 AC。但掌握分治/线段树的思路,可以帮助你应对更多变形题目。


总结

  • Kadane 代码简短、效率极高。
  • 分治 思想经典,体现了"分而治之"及区间合并的通用技巧。
  • 注意处理全负数边界情况,确保子数组非空。
相关推荐
什巳1 小时前
JAVA练习277- 找到字符串中所有字母异位
java·开发语言·算法·leetcode
机器学习之心1 小时前
基于遗传粒子群混合算法的无人机三维路径规划:Matlab 实现与多算法对比分析
算法·matlab·无人机·无人机三维路径规划·遗传粒子群混合算法
本王是暴君1 小时前
AutoMem:把 Agent Memory 变成可训练的记忆管理技能
人工智能·算法·数据挖掘
zmzb01032 小时前
C++课后习题训练记录Day154
数据结构·c++·算法
木木子227 小时前
# 密码强度检测深度解析:正则表达式实时分析、多维度评分算法与可视化反馈
mysql·算法·华为·正则表达式·harmonyos
Sw1zzle10 小时前
算法入门(四):二叉树 - 递归遍历三件套
算法·leetcode
万法若空10 小时前
【算法-查找】查找算法
java·数据结构·算法
海石10 小时前
子树怎么找?树的3种遍历方式来帮忙!
算法·leetcode
海石10 小时前
难度分 1588:思路 + 技巧 = AC
算法·leetcode