(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 代码简短、效率极高。
  • 分治 思想经典,体现了"分而治之"及区间合并的通用技巧。
  • 注意处理全负数边界情况,确保子数组非空。
相关推荐
带多刺的玫瑰4 小时前
Leecode#4刷题之寻找两个正序数组的中位数
java·前端·算法
土司大王4 小时前
LeetCode hot100——除了自身以外数组的乘积
数据结构·算法·leetcode
IvanCodes4 小时前
RAG 实战教程(三):向量数据库检索算法,KNN、IVF、HNSW 与 Faiss 实战
人工智能·算法·agent
阿无,5 小时前
布隆过滤器
java·算法·哈希算法
徒慕风流5 小时前
激光雷达回波信号滤波与时刻鉴别算法:原理、代码实现与仿真验证
python·算法·激光雷达
这料鬼有毒5 小时前
二刷hot100-70.爬楼梯
算法
CIO_Alliance5 小时前
AI算法系列(2)| 决策树与随机森林XGBoost:企业AI化转型中的树模型选型逻辑
大数据·人工智能·算法·决策树·系统集成·企业cio联盟·企业级ai化转型
ZC跨境爬虫6 小时前
LeetCode 13. 罗马数字转整数(多解法详解 + Java Python 实现)
java·python·leetcode
.道阻且长.6 小时前
10.LeetCode算法习题讲解--滑动窗口--最大连续为一的个数
算法·leetcode·职场和发展
学习中.........7 小时前
Karpathy nanoGPT 教程到底讲了什么
人工智能·算法·语言模型