当前位置: 首页 > news >正文

【力扣100题】56.最大子数组和

题目描述

给你一个整数数组 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 <= 10^5,-10^4 <= nums[i] <= 10^4 进阶:如果已经实现 O(n) 解法,尝试使用分治法

解题思路总览

方法核心思想时间复杂度空间复杂度备注
暴力枚举枚举所有子数组O(n^2)O(1)会超时
动态规划dp[i] = max(dp[i-1] + num, num)O(n)O(n)一维 DP
贪心(Kadane)pref = max(pref + num, num)O(n)O(1)最优解
分治法递归处理左右和跨越中点的情况O(n)O(logn)进阶要求

一、贪心算法(Kadane 算法,推荐)

核心思想

遍历数组,维护两个变量:

  • pref:以当前元素结尾的最大子数组和
  • ans:遍历过程中pref的最大值,即最终答案

关键决策:对于每个元素 num,是把它加入之前的子数组,还是从它开始重新作为一个新子数组?

如果 pref + num < num,说明之前的累加会减少和,不如从 num 重新开始 如果 pref + num >= num,把 num 加入子数组

代码实现

classSolution{public:intmaxSubArray(vector<int>&nums){intpref=0;// 以当前元素结尾的最大子数组和intans=nums[0];// 全局最大子数组和for(autonum:nums){// 决策:是从 num 重新开始,还是把 num 加入之前的子数组?pref=max(pref+num,num);// 更新答案ans=max(ans,pref);}returnans;}};

二、算法流程图

以 nums = [-2,1,-3,4,-1,2,1,-5,4] 为例

遍历过程: 初始状态: pref = 0 ans = nums[0] = -2 i=0, num=-2: pref = max(0 + (-2), -2) = max(-2, -2) = -2 ans = max(-2, -2) = -2 子数组: [-2] i=1, num=1: pref = max(-2 + 1, 1) = max(-1, 1) = 1 ans = max(-2, 1) = 1 子数组: [1] i=2, num=-3: pref = max(1 + (-3), -3) = max(-2, -3) = -2 ans = max(1, -2) = 1 子数组: [1, -3]? 不对,重新开始所以是 [-3] i=3, num=4: pref = max(-2 + 4, 4) = max(2, 4) = 4 ans = max(1, 4) = 4 子数组: [4] i=4, num=-1: pref = max(4 + (-1), -1) = max(3, -1) = 3 ans = max(4, 3) = 4 子数组: [4, -1] i=5, num=2: pref = max(3 + 2, 2) = max(5, 2) = 5 ans = max(4, 5) = 5 子数组: [4, -1, 2] i=6, num=1: pref = max(5 + 1, 1) = max(6, 1) = 6 ans = max(5, 6) = 6 子数组: [4, -1, 2, 1] <- 最大和 6! i=7, num=-5: pref = max(6 + (-5), -5) = max(1, -5) = 1 ans = max(6, 1) = 6 子数组: [4, -1, 2, 1, -5] i=8, num=4: pref = max(1 + 4, 4) = max(5, 4) = 5 ans = max(6, 5) = 6 子数组: [4] 最终答案:ans = 6 最长子数组:[4, -1, 2, 1]

三、逐行解析(对照原题代码)

intmaxSubArray(vector<int>&nums){intpref=0;// 初始化为 0,表示"空子数组的和"intans=nums[0];// ans 必须初始化为 nums[0],因为子数组最少包含一个元素for(autonum:nums){// pref: 以 num 结尾的最大子数组和// max(pref + num, num) 的含义:// - pref + num : 把 num 加入之前的子数组// - num : 从 num 重新开始(之前的累加不如从头来)pref=max(pref+num,num);// ans: 历史上最大的 pref,即全局最大子数组和ans=max(ans,pref);}returnans;}

关键点解释

语句含义
pref当前遍历到的元素结尾的最大子数组和
pref = max(pref + num, num)决策是否延续之前的子数组
ans记录遍历过程中最大的pref
ans = nums[0]子数组最少包含一个元素,所以 ans 不能初始化为 0

为什么 pref 可以初始化为 0?

因为 max(0 + num, num) = max(num, num) = num 对于第一个元素 num = -2: pref = max(0 + (-2), -2) = -2 <- 正确 这相当于:从第一个元素开始,之前的"空子数组"没有贡献,所以 pref = num

四、图解贪心选择

nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4] pref 变化过程(折线图): 6 | ____ 5 | ____/ \____ 4 | ____/ * ---- ans = 6 3 | ____/ * ---- 2 | ____/ * ---- 1 | __/ * ---- 0 |/ * * ---- -1 | * * ---- -2 | * * ---- -3 | * ---- -4 +------------------------------ 0 1 2 3 4 5 6 7 8 * = pref 在该位置的取值 关键决策点: - i=1: pref=-2 -> pref=1 (重新开始有利) - i=2: pref=1 -> pref=-2 (重新开始有利) - i=3: pref=-2 -> pref=4 (重新开始有利) - i=6: pref=5 -> pref=6 (延续,加入1)

五、动态规划解法(对比)

思路

dp[i]= 以 nums[i] 结尾的最大子数组和

状态转移方程

dp[i] = max(dp[i-1] + nums[i], nums[i])
classSolution{public:intmaxSubArray(vector<int>&nums){intn=nums.size();vector<int>dp(n);dp[0]=nums[0];intans=dp[0];for(inti=1;i<n;i++){dp[i]=max(dp[i-1]+nums[i],nums[i]);ans=max(ans,dp[i]);}returnans;}};

与贪心的关系

贪心:pref = max(pref + num, num) DP: dp[i] = max(dp[i-1] + nums[i], nums[i]) pref 就是 dp[i],只是贪心只用一个变量,不需要数组 时间复杂度:O(n) -> 相同 空间复杂度:O(n) -> 贪心 O(1) 更优

六、分治法(进阶)

思路

将数组分成左右两部分,最大子数组有三种情况:

  1. 完全在左半部分
  2. 完全在右半部分
  3. 跨越中点(包含左部分末尾和右部分开头)

代码实现

classSolution{public:intmaxSubArray(vector<int>&nums){returndivideConquer(nums,0,nums.size()-1);}private:intdivideConquer(vector<int>&nums,intleft,intright){if(left==right)returnnums[left];intmid=(left+right)/2;// 1. 左半部分的最大子数组和intleftSum=divideConquer(nums,left,mid);// 2. 右半部分的最大子数组和intrightSum=divideConquer(nums,mid+1,right);// 3. 跨越中点的最大子数组和// 从中点向左扫,找到最大和intcrossLeft=nums[mid];inttemp=0;for(inti=mid;i>=left;i--){temp+=nums[i];crossLeft=max(crossLeft,temp);}// 从中点+1向右扫,找到最大和intcrossRight=nums[mid+1];temp=0;for(inti=mid+1;i<=right;i++){temp+=nums[i];crossRight=max(crossRight,temp);}intcrossSum=crossLeft+crossRight;// 返回三种情况的最大值returnmax(max(leftSum,rightSum),crossSum);}};

复杂度

方法时间复杂度空间复杂度
贪心O(n)O(1)
动态规划O(n)O(n)
分治法O(n log n)O(logn)

复杂度分析总结

方法时间复杂度空间复杂度备注
暴力枚举O(n^2)O(1)会超时
动态规划O(n)O(n)一维 DP
贪心(Kadane)O(n)O(1)最优解
分治法O(n log n)O(logn)进阶要求

面试追问 FAQ

问题回答要点
Q: 为什么可以这样做贪心选择?pref + num < num时,说明之前的子数组对增大和没有帮助,反而会减少。从 num 重新开始能得到更大的子数组和
Q: 为什么 ans 要初始化为 nums[0]?因为子数组最少包含一个元素,不能为空。如果初始化为 0,而 nums 全为负数,答案会错误
Q: 如果数组全为负数会怎样?pref 会一直选择 max(负+负, 负) = 负数,ans 会记录最大的那个负数,正确
Q: 分治法的时间复杂度为什么是 O(n log n)?每层遍历需要 O(n),层数是 O(log n),所以总复杂度 O(n log n)
Q: Kadane 算法的名称来源?由日本计算机科学家今道明彦(Kadane)在 1984 年提出

相关题目

题目编号题目名称难度核心差异
53最大子数组和中等基础题,求最大和
918环形子数组的最大和中等数组首尾相连
152乘积最大子数组中等乘积而非求和,需处理负数
1749统计所有子数组中最大和困难所有连续子数组的和
238除自身以外数组的乘积中等前缀后缀技巧

总结

要点内容
核心思想贪心:以当前元素结尾的最大子数组和 = max(延续, 重新开始)
关键变量pref = 以 num 结尾的最大子数组和;ans = 全局最大和
时间复杂度O(n),只需遍历一次
空间复杂度O(1),只用一个变量
边界处理ans 必须初始化为 nums[0],因为子数组不能为空
与 DP 对比Kadane 是 DP 的空间优化版本,两者本质相同

Kadane 算法是处理最大子数组和的标准解法,掌握后可以轻松解决环形子数组、乘积最大等变种问题。


http://www.cnnetsun.cn/news/2595745.html

相关文章:

  • 千问 LeetCode 2713. 矩阵中严格递增的单元格数 Java实现
  • 终极Mac清理指南:Pearcleaner彻底卸载应用并释放存储空间
  • 设备可靠性分析入门:用威布尔分布预测你的服务器硬盘还能撑多久
  • 告别环境配置烦恼:用Shell脚本一键部署Synopsys VCS 2018 + Verdi + SCL
  • 华为防火墙USG6309E开局实战:从零构建安全网络通道
  • ABAQUS进阶实战:复杂结构六面体网格高效剖分策略
  • 创业团队如何进行技术规划
  • LizzieYzy:免费开源的围棋AI分析助手,打造你的职业级围棋教练
  • 跟我学UDS(ISO14229) ———— 0x36(TransferData)的实战解析与容错机制
  • Logisim门电路实战指南:从真值表到复杂逻辑构建
  • Spring Cloud 详解(一篇文章带你玩转各种技术)
  • 终极指南:如何免费解锁《艾尔登法环》帧率限制,畅享高帧率游戏体验
  • 英雄联盟终极智能助手:League Akari 完全使用指南
  • 如何快速掌握MoveIt2:面向初学者的完整ROS 2运动规划框架指南
  • 避开这些坑!ADNI数据预处理前必须搞懂的文档:DocumentSummary.csv与ARM.csv详解
  • 【GNN图神经网络】从聚类系数看社交网络中的“小圈子”效应
  • FModel:虚幻引擎游戏资源逆向工程与资产提取技术深度解析
  • 从`<svg>`到`<use>`:解锁HTML中SVG图标系统的完整工作流
  • libaom 源码分析:运动搜索过程和 pattern_search 函数
  • 对比按量计费与Token Plan在Taotoken平台的实际支出感受
  • 别再只用TrailRenderer了!用Unity的LineRenderer实现更丝滑的切水果刀痕(附完整C#脚本)
  • 鸣潮自动化实战指南:基于图像识别的智能辅助工具深度解析
  • 如何快速掌握Nginx配置文件格式化:面向开发者的完整指南
  • 突破百度网盘限速:基于Python的下载链接解析技术方案
  • 免费文档下载终极方案:解锁百度文库、道客巴巴等30+平台限制
  • JSON操作封装
  • 自托管AI智能体框架TALOS:本地部署、自定义工具与安全实践指南
  • 图片去水印用什么工具好用|2026 免费图片去水印工具推荐与实测对比
  • 2026 图片去水印工具推荐|免费图片去水印工具实测有哪些好用的
  • F411-WeAct实战:IIC驱动SSD1306 OLED显示模块(0.96寸)