(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 代码简短、效率极高。
  • 分治 思想经典,体现了"分而治之"及区间合并的通用技巧。
  • 注意处理全负数边界情况,确保子数组非空。
相关推荐
u01110267511 分钟前
图片生成任务的日志记录与敏感信息取舍
前端·图像处理·算法·ai作画
花间相见40 分钟前
【计算基础|MySQL08】索引(上):为什么是 B+ 树
数据结构·mysql·算法·架构
吠品1 小时前
Flutter 工程配本地 Maven 引 AAR,仓库声明为啥不生效
c语言·人工智能·算法
罗湖老棍子1 小时前
【例题1】黑暗城堡(信息学奥赛一本通- P1486)
算法·图论·最短路·dijkstra·最小生成树·最短路径生成树
hetao17338371 小时前
2026-10-20 hetao1733837 的刷题记录
c++·算法
wenyq72 小时前
LeetCode 123. Best Time to Buy and Sell Stock III
leetcode
碧海银沙音频科技研究院3 小时前
基于ATS308X的无线语音交互对话功能实现
人工智能·嵌入式硬件·深度学习·算法
尹玉玺3 小时前
从原子残片到天道公理:以德谟克利特思想全貌,重新定界西方唯物认知的历史层级与边界
人工智能·算法·架构
青少儿编程课堂3 小时前
蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级:差分数组与区间修改实战解析
c++·python·算法·bfs·信息学竞赛