news 2026/4/15 14:41:25

【Hot100|14-LeetCode53. 最大子数组和】

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Hot100|14-LeetCode53. 最大子数组和】

一、问题理解

问题描述

给定一个整数数组nums,找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例

text

输入:nums = [-2,1,-3,4,-1,2,1,-5,4] 输出:6 解释:连续子数组 [4,-1,2,1] 的和最大,为 6 输入:nums = [1] 输出:1 输入:nums = [5,4,-1,7,8] 输出:23

二、核心思路:前缀和 + 最小前缀和追踪

暴力解法的局限性

枚举所有子数组并计算和,时间复杂度为 O(n²),效率极低。

前缀和优化思路

  1. 前缀和定义prefix[i]表示从数组开头到第i个元素(不包括第i个)的和

  2. 核心观察

    • 子数组nums[i:j]的和 =prefix[j] - prefix[i]

    • 对于每个位置j,要找到prefix[j] - prefix[i]的最大值,就是要找到prefix[i]的最小值(其中i < j

  3. 优化策略

    • 遍历数组,计算前缀和

    • 同时维护到当前位置的最小前缀和

    • 用当前前缀和减去最小前缀和,得到以当前位置为结尾的最大子数组和

    • 更新全局最大和

三、代码逐行解析

python

from typing import List import math class Solution: def maxSubArray(self, nums: List[int]) -> int: # 1. 初始化 # ans: 存储最大子数组和,初始化为负无穷,确保第一个元素可以更新它 ans = -math.inf # min_pre_sum: 记录到当前位置的最小前缀和,初始为0(空数组的前缀和) min_pre_sum = 0 # pre_sum: 当前前缀和,初始为0 pre_sum = 0 # 2. 遍历数组 for x in nums: # 2.1 更新当前前缀和 pre_sum += x # 2.2 计算以当前位置为结尾的最大子数组和 # 当前前缀和 - 之前的最小前缀和 = 以当前位置为结尾的最大子数组和 ans = max(ans, pre_sum - min_pre_sum) # 2.3 更新最小前缀和 # 注意:先计算ans再更新min_pre_sum,确保min_pre_sum是当前位置之前的最小值 min_pre_sum = min(min_pre_sum, pre_sum) # 3. 返回结果 return ans

四、关键步骤详解

1. 为什么需要min_pre_sum

对于任意位置j,以j结尾的最大子数组和为:

text

max_sum_ending_at_j = max(prefix[j] - prefix[i]) for all i < j

由于prefix[j]是固定的,要使差值最大,就要使prefix[i]最小。因此,我们只需要维护到当前位置的最小前缀和即可。

2. 为什么min_pre_sum初始化为 0?

  • 空数组的前缀和为 0

  • 当数组只有一个元素时,最大子数组和就是该元素本身

  • 对于第一个元素xpre_sum = xans = max(-∞, x - 0) = x

  • 这样处理确保了单个元素的情况也能正确计算

3. 为什么先计算ans再更新min_pre_sum

我们需要的是当前位置之前的最小前缀和,不包括当前位置。

  • 如果先更新min_pre_sum,可能会用到当前前缀和,这会导致计算出的子数组可能为空

  • 先计算ans确保min_pre_sum是严格在当前位置之前的最小值

五、算法步骤演示

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

步骤xpre_summin_pre_sumpre_sum - min_pre_sumans
初始-00--∞
1-2-20-2max(-∞, -2) = -2
更新--2min(0, -2) = -2--2
21-1-21max(-2, 1) = 1
更新--1min(-2, -1) = -2-1
3-3-4-2-2max(1, -2) = 1
更新--4min(-2, -4) = -4-1
440-44max(1, 4) = 4
更新-0min(-4, 0) = -4-4
5-1-1-43max(4, 3) = 4
更新--1min(-4, -1) = -4-4
621-45max(4, 5) = 5
更新-1min(-4, 1) = -4-5
712-46max(5, 6) = 6
更新-2min(-4, 2) = -4-6
8-5-3-41max(6, 1) = 6
更新--3min(-4, -3) = -4-6
941-45max(6, 5) = 6

最终结果:6

六、与其他解法的对比

1. 动态规划(Kadane算法)

python

def maxSubArray_kadane(nums): max_sum = nums[0] current_sum = nums[0] for i in range(1, len(nums)): current_sum = max(nums[i], current_sum + nums[i]) max_sum = max(max_sum, current_sum) return max_sum

复杂度

  • 时间复杂度:O(n)

  • 空间复杂度:O(1)

与本题解法的关系

  • Kadane算法是动态规划的思想:dp[i]表示以i结尾的最大子数组和

  • 本题解法是前缀和的思想:最大子数组和 = 当前前缀和 - 最小前缀和

  • 两种方法本质相同,都达到了 O(n) 时间复杂度和 O(1) 空间复杂度

2. 分治法

python

def maxSubArray_divide_conquer(nums): def helper(left, right): if left == right: return nums[left] mid = (left + right) // 2 # 左半部分最大子数组和 left_max = helper(left, mid) # 右半部分最大子数组和 right_max = helper(mid + 1, right) # 跨中点的最大子数组和 left_sum = nums[mid] left_temp = nums[mid] for i in range(mid - 1, left - 1, -1): left_temp += nums[i] left_sum = max(left_sum, left_temp) right_sum = nums[mid + 1] right_temp = nums[mid + 1] for i in range(mid + 2, right + 1): right_temp += nums[i] right_sum = max(right_sum, right_temp) cross_max = left_sum + right_sum return max(left_max, right_max, cross_max) return helper(0, len(nums) - 1)

复杂度

  • 时间复杂度:O(n log n)

  • 空间复杂度:O(log n)(递归栈空间)

七、复杂度分析

时间复杂度:O(n)

  • 只遍历数组一次

  • 每次循环执行常数时间操作(加法、减法、比较)

空间复杂度:O(1)

  • 只使用了常数个额外变量(ansmin_pre_sumpre_sum

八、优化技巧与变体

1. 处理全负数数组

python

def maxSubArray_all_negative(nums): # 如果确定数组全为负数,可以先找到最大值 max_val = max(nums) if max_val < 0: return max_val # 否则使用原算法 ans = -math.inf min_pre_sum = 0 pre_sum = 0 for x in nums: pre_sum += x ans = max(ans, pre_sum - min_pre_sum) min_pre_sum = min(min_pre_sum, pre_sum) return ans

2. 返回最大子数组本身

python

def maxSubArray_with_subarray(nums): ans = -math.inf min_pre_sum = 0 pre_sum = 0 start = 0 end = 0 temp_start = 0 for i, x in enumerate(nums): pre_sum += x if pre_sum - min_pre_sum > ans: ans = pre_sum - min_pre_sum start = temp_start end = i if pre_sum < min_pre_sum: min_pre_sum = pre_sum temp_start = i + 1 return ans, nums[start:end+1] # 示例 nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4] max_sum, subarray = maxSubArray_with_subarray(nums) print(f"最大和: {max_sum}, 子数组: {subarray}") # 输出: 最大和: 6, 子数组: [4, -1, 2, 1]

3. 处理环形数组(LeetCode 918)

python

def maxSubarraySumCircular(nums): # 环形数组的最大子数组和有两种情况: # 1. 最大子数组在数组中间(非环形) # 2. 最大子数组跨越数组首尾(环形) # 情况2 = 数组总和 - 最小子数组和 total = sum(nums) max_sum = nums[0] min_sum = nums[0] cur_max = nums[0] cur_min = nums[0] for i in range(1, len(nums)): cur_max = max(nums[i], cur_max + nums[i]) max_sum = max(max_sum, cur_max) cur_min = min(nums[i], cur_min + nums[i]) min_sum = min(min_sum, cur_min) # 特殊情况:如果所有数都是负数,则max_sum就是最大负数 if max_sum < 0: return max_sum # 环形情况的最大值 = max(非环形最大和, 总和 - 最小和) return max(max_sum, total - min_sum)

九、常见问题与解答

Q1: 如果数组全为负数,算法还正确吗?

A1: 正确。算法会返回最大的负数。

  • 例如nums = [-2, -1]

    • 第一步:pre_sum = -2,ans = -2,min_pre_sum = -2

    • 第二步:pre_sum = -3,ans = max(-2, -3 - (-2)) = -1,min_pre_sum = -3

    • 返回-1,这是正确的(最大子数组是[-1],和为-1

Q2: 为什么min_pre_sum初始化为 0 而不是nums[0]

A2: 初始化为 0 可以正确处理只有一个元素的情况。

  • 如果初始化为nums[0],对于nums = [1]

    • pre_sum = 1,ans = 1 - 1 = 0,这是错误的

  • 初始化为 0:

    • pre_sum = 1,ans = 1 - 0 = 1,这是正确的

Q3: 这个算法和 Kadane 算法哪个更好?

A3: 两者时间复杂度都是 O(n),空间复杂度都是 O(1)。

  • Kadane 算法更直观,直接表达了动态规划的思想

  • 前缀和算法更数学化,展示了问题本质

  • 在实际应用中,Kadane 算法更常用,但两种方法都是正确的

十、相关题目

1. LeetCode 152. 乘积最大子数组

python

def maxProduct(nums): if not nums: return 0 max_prod = nums[0] min_prod = nums[0] result = nums[0] for i in range(1, len(nums)): # 由于负负得正,需要同时维护最大和最小乘积 temp_max = max(nums[i], max_prod * nums[i], min_prod * nums[i]) temp_min = min(nums[i], max_prod * nums[i], min_prod * nums[i]) max_prod, min_prod = temp_max, temp_min result = max(result, max_prod) return result

2. LeetCode 918. 环形子数组的最大和(前面已提到)

3. LeetCode 121. 买卖股票的最佳时机

python

def maxProfit(prices): if not prices: return 0 min_price = prices[0] max_profit = 0 for price in prices[1:]: # 更新最大利润 max_profit = max(max_profit, price - min_price) # 更新最低价格 min_price = min(min_price, price) return max_profit

十一、总结

核心要点

  1. 前缀和是关键:将子数组和问题转化为前缀和之差问题

  2. 最小前缀和追踪:对于每个位置,最大子数组和 = 当前前缀和 - 之前的最小前缀和

  3. 时间复杂度优化:从暴力解法的 O(n²) 优化到 O(n)

算法步骤

  1. 初始化ans为负无穷,min_pre_sum为 0,pre_sum为 0

  2. 遍历数组中的每个元素:

    • 更新当前前缀和pre_sum

    • 更新答案ans = max(ans, pre_sum - min_pre_sum)

    • 更新最小前缀和min_pre_sum = min(min_pre_sum, pre_sum)

  3. 返回ans

适用场景

  • 需要找到连续子数组的最大和

  • 数组可能包含负数

  • 要求时间复杂度为 O(n)

扩展思考

这个解法展示了如何将看似复杂的问题(最大子数组和)转化为简单的数学关系(前缀和之差),通过追踪最小值来优化计算。这种思想可以应用到许多其他问题中,如:

  • 最小子数组和

  • 子数组和接近某个目标值

  • 二维数组的最大子矩阵和

掌握前缀和+最小前缀和追踪的解法,不仅能够解决最大子数组和问题,还能够解决一系列类似的子数组和问题,是面试中非常重要的算法技巧。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/20 7:15:31

Java计算机毕设之基于Java+MySQL+SpringBoot幼儿园管理系统基于springboot的幼儿园管理系统(完整前后端代码+说明文档+LW,调试定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/4/8 19:35:58

丞相锦囊:27 届网安大学生就业迷茫?速看破局妙计!

丞相言: 当27届网络安全大学生&#xff0c;对就业迷茫时&#xff0c;可打开此条锦囊妙计&#xff01; “学长&#xff01;学长&#xff01;27届网安大学生现在还只有一段实习怎么办&#xff1f;” “学长&#xff01;我更紧急&#xff0c;我27届还没开始学的&#xff01;&…

作者头像 李华
网站建设 2026/4/10 6:20:51

饮料灌装流水线控制画面【程序与文档】(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码

饮料灌装流水线控制画面【程序与文档】(设计源文件万字报告讲解)&#xff08;支持资料、图片参考_相关定制&#xff09;_文章底部可以扫码 西门子PLC程序设计饮料罐装控制要求如下图所示&#xff0c;西门子1200博途V15(博途版本V15及以上都可以打开) 包括梯形图程序、触摸屏仿真…

作者头像 李华
网站建设 2026/4/7 5:27:10

Python AST 实战:自动移除 print / head / show / to_html 等无用代码行

在数据分析、Notebook 转生产代码、AI 生成代码清洗等场景中&#xff0c;我们经常需要&#xff1a;自动删除 print()、DataFrame.head()、plt.show()、to_html() 等仅用于展示的代码&#xff0c;而不影响业务逻辑正则不可靠&#xff0c;AST 才是王道。 本文将通过一个完整可运行…

作者头像 李华
网站建设 2026/4/15 5:47:58

Flutter × OpenHarmony 跨端开发:变量与数据结构实战解析

文章目录 Flutter OpenHarmony 跨端开发&#xff1a;变量与数据结构实战解析前言背景Flutter OpenHarmony 跨端开发介绍开发核心代码&#xff08;详细解析&#xff09;1. 页面和状态定义2. 数据模型设计3. 状态变量和初始化4. UI 构建与数据绑定 心得总结 Flutter OpenHarmo…

作者头像 李华
网站建设 2026/4/8 18:39:58

字节4面通过,我可以跟面试官要30K吗?

春招&#xff0c;秋招&#xff0c;社招&#xff0c;我们程序员的面试之路&#xff0c;是挺难的&#xff0c;过了HR&#xff0c;还得被技术面&#xff0c;小编在去各个大厂面试的时候&#xff0c;经常是通宵睡不着觉&#xff0c;头发都脱了一大把&#xff0c;还好最终侥幸能够入…

作者头像 李华