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

【LeetCode热题100道笔记+动画】最大子数组和

题目描述

给你一个整数数组 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 <= 105
-104 <= nums[i] <= 104

进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的 分治法 求解。

思考一(动态规划)

以每个元素为结尾的最大子数组和,可通过两种选择确定:要么将当前元素加入前一个子数组(即 dp[i-1] + nums[i]),要么以当前元素为起点重新构建子数组(即 nums[i]),取两者较大值作为 dp[i]。遍历数组时同步更新全局最大值,最终即为结果。

代码

/*** @param {number[]} nums* @return {number}*/
var maxSubArray = function(nums) {let dp = Array(nums.length).fill(0); // dp[i] 表示第个数结尾子数组的最大子数组和dp[0] = nums[0];let ans = nums[0];for (let i = 1; i < nums.length; i++) {dp[i] = Math.max(dp[i-1] + nums[i], nums[i]);ans = Math.max(ans, dp[i]);}return ans;
};

优化

动态规划中无需存储所有位置的子数组和,只需保留上一个位置结尾的最大子数组和maxVal),即可递推当前位置的结果。通过这种滚动更新的方式,空间复杂度可从 O(n)O(n)O(n) 优化至 O(1)O(1)O(1)

代码(动态规划优化)

/*** @param {number[]} nums* @return {number}*/
var maxSubArray = function(nums) {let ans = nums[0], maxVal = nums[0]; // ans记录全局最大和,maxVal记录上一位置结尾的最大和for (let i = 1; i < nums.length; i++) {maxVal = Math.max(maxVal + nums[i], nums[i]); // 递推当前位置的最大和ans = Math.max(ans, maxVal); // 更新全局最大值}return ans;
};

思考二(分治法)

分治法求解最大子数组和的核心思路是:将原数组递归分解为更小的子数组,分别求解每个子数组的最大和,再通过合并子问题的解得到原问题的答案。

具体来说,数组的最大子数组只有三种可能:要么完全在左半部分,要么完全在右半部分,要么横跨左右两部分(包含中间元素)。因此,递归过程中只需计算这三种情况的最大值,即可得到当前数组的最大子数组和。该方法的难点在于准确计算“横跨左右的最大子数组和”,以及处理递归的边界条件。

算法过程

  1. 分解
    将当前数组区间 [left, right] 以枢轴 pivot(中间位置)分为左子数组 [left, pivot-1] 和右子数组 [pivot+1, right],递归求解左右子数组的最大子数组和(分别记为 leftMaxrightMax)。

  2. 求解横跨左右的最大子数组和
    从枢轴 pivot 向左侧扩展,计算包含枢轴的左半部分最大和(即从枢轴向左累加,取过程中的最大值);
    从枢轴 pivot 向右侧扩展,计算包含枢轴的右半部分最大和;
    两者相加(若右半部分最大和为正,否则只取左半部分),得到横跨左右的最大和 middleMax

  3. 合并
    当前数组的最大子数组和为 leftMaxrightMaxmiddleMax 中的最大值,作为递归返回结果。

  4. 边界条件
    当子数组长度为1时(left == right),最大和即为该元素本身。

代码

/*** @param {number[]} nums* @return {number}*/
var maxSubArray = function(nums) {const len = nums.length;if (len === 1) {return nums[0];}const divideAndConquer = function(left, pivot, right) {// 处理枢轴左边子数组let leftMax = -Infinity;if (pivot - left <= 1) { // 左边数组就包含一个元素leftMax = nums[left];} else {leftMax = divideAndConquer(left, left + Math.floor((pivot-left)/2), pivot);}// 处理枢轴右边子数组let rightMax = -Infinity;if (right -pivot <= 1) {// 右边数组就包含一个元素rightMax = nums[right];} else {rightMax = divideAndConquer(pivot, pivot + Math.floor((right - pivot)/2), right);}// 处理中间跨左右子数组的部分(包含pivot)let middleMax = nums[pivot];let maxVal = nums[pivot];for (let i = pivot-1; i >= left; i--) {maxVal += nums[i];middleMax = Math.max(middleMax, maxVal);}maxVal = 0;let sumRight = 0;for (let i = pivot+1; i <= right; i++) {maxVal += nums[i];sumRight = Math.max(sumRight, maxVal);}if (sumRight > 0) {middleMax += sumRight;}return Math.max(leftMax, middleMax, rightMax);};return divideAndConquer(0, Math.floor(len/2), len-1);    
};

可视化

在这里插入图片描述

http://www.dtcms.com/a/343886.html

相关文章:

  • 任务同步和锁
  • 基于django/python的服装销售系统平台/服装购物系统/基于django/python的服装商城
  • sqli-labs通关笔记-第61关 GET字符型报错注入(单引号双括号闭合 限制5次探测机会)
  • 基于Django的学校实验室预约管理系统/基于python的实验室管理系统的设计与实现#python#django#FLASK
  • JAVA基础-java虚拟机
  • uniapp googlepay支付 内购项目
  • 豆包AI PPT与秒出PPT对比评测:谁更适合你?
  • 计算机毕设选题推荐 基于Spark的家庭能源消耗智能分析与可视化系统 基于机器学习的家庭能源消耗预测与可视化系统源码
  • Python办公之Excel(openpyxl)、PPT(python-pptx)、Word(python-docx)
  • 2026年计算机毕设推荐:基于大数据的慢性肾病数据可视化分析系统技术选型指南【Hadoop、spark、python】
  • 使用PPT进行科研绘图过程中常用的快捷键
  • 日志logging学习(1)
  • Gemini 2.5 Flash-Lite与 DeepSeek-V3 深度对比:谁在性价比上更胜一筹?
  • 【typenum】 21 类型级别计算最大公约数(Gcd)
  • map和set的使⽤
  • 52 C++ 现代C++编程艺术1-禁止隐式转换关键字explicit
  • Qt中用于图像缩放的核⼼⽅法QPixmap::scaled
  • 编写Linux下设备驱动时两种方案:内核态驱动开发和用户态驱动开发
  • --- 使用OpenFeign来优雅的对服务进行调用 ---
  • vue2怎么修改el-table样式
  • 金融风控AI引擎:实时反欺诈系统的架构设计与实现
  • CTFSHOW | 其他篇题解(二)web417 - web437
  • 进程间通信-IPC机制
  • 【开发日记】SpringBoot 实现支持多个微信小程序的登录
  • 初始数据结构——反射、枚举与Lambda的奇幻冒险
  • 如何理解AP服务发现协议中“如果某项服务需要在多个网络接口上提供,则应为每个网络接口使用一个独立的服务器服务实例。”?
  • 《Linux 网络编程一:网络编程导论及UDP 服务器的创建与数据接收》
  • “我 / 店模式” 靠联盟 + 积分破局,实现三方共赢!
  • 【Oracle】内存管理实战指南:ASMM vs AMM 配置全解析
  • Rust Web开发指南 第一章