冬眠的笔记
首页文章分类书单项目关于
冬眠
X

© 2026 冬眠的笔记 · 用文字记录思考,用思考改变生活

首页>文章>面试
算法数组动态规划LeetCode

最大子数组和

最大子数组和的多种解法对比,包含 Kadane 算法和分治法实现

冬眠
冬眠
专注于技术、阅读与思考
2025-11-17
发布日期
16 min read
阅读时长
浏览量
最大子数组和

1. 问题描述

给你一个整数数组 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

2. 核心难点分析

2.1 问题本质

  • 连续性要求:必须是连续的子数组,不能跳跃选择元素
  • 动态决策:每个位置都需要决定是继续扩展当前子数组还是重新开始
  • 负数处理:如何处理负数对最大和的影响

2.2 关键思路

  • 局部最优 vs 全局最优:当前位置的最大子数组和 vs 整个数组的最大子数组和
  • 状态转移:当前元素是加入前面的子数组还是自成一个新的子数组开始
  • 边界处理:单个元素、全负数数组等特殊情况

3. 解法分析

3.1 暴力解法

思路:枚举所有可能的子数组,计算每个子数组的和,找出最大值。

时间复杂度:O(n³) 或 O(n²) 空间复杂度:O(1)

def maxSubArray_brute_force(nums):
    n = len(nums)
    max_sum = float('-inf')
    
    # 枚举所有子数组
    for i in range(n):
        current_sum = 0
        for j in range(i, n):
            current_sum += nums[j]
            max_sum = max(max_sum, current_sum)
    
    return max_sum

3.2 动态规划解法

思路:定义 dp[i] 表示以第 i 个元素结尾的最大子数组和。

状态转移方程:

dp[i] = max(nums[i], dp[i-1] + nums[i])

时间复杂度:O(n) 空间复杂度:O(n) 或 O(1)

3.3 Kadane算法(最优解)

思路:动态规划的空间优化版本,只用一个变量记录当前最大子数组和。

核心思想:

  • 如果当前累积和为负数,则丢弃,重新开始
  • 否则继续累积,并更新全局最大值

3.4 分治法

思路:将数组分为两部分,最大子数组和可能在左半部分、右半部分或跨越中点。

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

4. 详细代码实现

4.1 Kadane算法(Python实现)

def maxSubArray(nums):
    """
    使用Kadane算法求最大子数组和
    
    Args:
        nums: List[int] - 输入数组
    
    Returns:
        int - 最大子数组和
    """
    if not nums:
        return 0
    
    # 当前子数组的最大和
    current_sum = nums[0]
    # 全局最大子数组和
    max_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

# 返回最大子数组的起始和结束位置
def maxSubArrayWithIndex(nums):
    """
    返回最大子数组和及其起始结束位置
    
    Returns:
        tuple: (最大和, 起始索引, 结束索引)
    """
    if not nums:
        return 0, -1, -1
    
    current_sum = nums[0]
    max_sum = nums[0]
    start = 0  # 最大子数组起始位置
    end = 0    # 最大子数组结束位置
    temp_start = 0  # 临时起始位置
    
    for i in range(1, len(nums)):
        if current_sum < 0:
            current_sum = nums[i]
            temp_start = i
        else:
            current_sum += nums[i]
        
        if current_sum > max_sum:
            max_sum = current_sum
            start = temp_start
            end = i
    
    return max_sum, start, end

4.2 Java实现

public class MaxSubArray {
    
    /**
     * Kadane算法求最大子数组和
     */
    public int maxSubArray(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        
        int currentSum = nums[0];
        int maxSum = nums[0];
        
        for (int i = 1; i < nums.length; i++) {
            // 核心决策:继续扩展 vs 重新开始
            currentSum = Math.max(nums[i], currentSum + nums[i]);
            maxSum = Math.max(maxSum, currentSum);
        }
        
        return maxSum;
    }
    
    /**
     * 动态规划实现
     */
    public int maxSubArrayDP(int[] nums) {
        int n = nums.length;
        int[] dp = new int[n];
        dp[0] = nums[0];
        int maxSum = dp[0];
        
        for (int i = 1; i < n; i++) {
            dp[i] = Math.max(nums[i], dp[i-1] + nums[i]);
            maxSum = Math.max(maxSum, dp[i]);
        }
        
        return maxSum;
    }
    
    /**
     * 分治法实现
     */
    public int maxSubArrayDivide(int[] nums) {
        return divideAndConquer(nums, 0, nums.length - 1);
    }
    
    private int divideAndConquer(int[] nums, int left, int right) {
        if (left == right) {
            return nums[left];
        }
        
        int mid = left + (right - left) / 2;
        
        // 左半部分最大子数组和
        int leftMax = divideAndConquer(nums, left, mid);
        // 右半部分最大子数组和
        int rightMax = divideAndConquer(nums, mid + 1, right);
        // 跨越中点的最大子数组和
        int crossMax = maxCrossingSum(nums, left, mid, right);
        
        return Math.max(Math.max(leftMax, rightMax), crossMax);
    }
    
    private int maxCrossingSum(int[] nums, int left, int mid, int right) {
        int leftSum = Integer.MIN_VALUE;
        int sum = 0;
        
        // 从中点向左扩展
        for (int i = mid; i >= left; i--) {
            sum += nums[i];
            leftSum = Math.max(leftSum, sum);
        }
        
        int rightSum = Integer.MIN_VALUE;
        sum = 0;
        
        // 从中点+1向右扩展
        for (int i = mid + 1; i <= right; i++) {
            sum += nums[i];
            rightSum = Math.max(rightSum, sum);
        }
        
        return leftSum + rightSum;
    }
}

5. 复杂度分析

5.1 时间复杂度对比

算法 时间复杂度 空间复杂度 优缺点
暴力法 O(n²) O(1) 简单直观,但效率低
动态规划 O(n) O(n) 思路清晰,需要额外空间
Kadane算法 O(n) O(1) 最优解,空间效率高
分治法 O(n log n) O(log n) 体现分治思想,但不是最优

5.2 详细分析

Kadane算法:

  • 时间复杂度:O(n) - 只需要遍历数组一次
  • 空间复杂度:O(1) - 只使用常数个变量
  • 最优性:在线性时间内解决问题,是该问题的最优解

6. 边界情况处理

6.1 特殊输入

def maxSubArray_robust(nums):
    # 空数组处理
    if not nums:
        return 0
    
    # 单元素数组
    if len(nums) == 1:
        return nums[0]
    
    # 全负数数组
    if all(x < 0 for x in nums):
        return max(nums)  # 返回最大的负数
    
    # 正常情况使用Kadane算法
    current_sum = nums[0]
    max_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

6.2 边界测试用例

# 测试用例
test_cases = [
    [],                          # 空数组
    [1],                         # 单元素正数
    [-1],                        # 单元素负数
    [-2, -1, -3],               # 全负数
    [1, 2, 3, 4, 5],            # 全正数
    [-2, 1, -3, 4, -1, 2, 1, -5, 4],  # 混合数组
    [0, 0, 0],                  # 全零数组
    [-1, 0, -2],                # 包含零的负数数组
]

for i, nums in enumerate(test_cases):
    if nums:  # 非空数组才测试
        result = maxSubArray_robust(nums)
        print(f"测试用例 {i+1}: {nums} -> {result}")

7. 相关变种问题

7.1 最大子数组乘积

def maxProduct(nums):
    """
    最大子数组乘积(考虑负数相乘变正数的情况)
    """
    if not nums:
        return 0
    
    max_prod = min_prod = result = nums[0]
    
    for i in range(1, len(nums)):
        if nums[i] < 0:
            max_prod, min_prod = min_prod, max_prod
        
        max_prod = max(nums[i], max_prod * nums[i])
        min_prod = min(nums[i], min_prod * nums[i])
        result = max(result, max_prod)
    
    return result

7.2 环形数组最大子数组和

def maxSubarraySumCircular(nums):
    """
    环形数组的最大子数组和
    """
    def kadane_max(arr):
        current = max_sum = arr[0]
        for i in range(1, len(arr)):
            current = max(arr[i], current + arr[i])
            max_sum = max(max_sum, current)
        return max_sum
    
    def kadane_min(arr):
        current = min_sum = arr[0]
        for i in range(1, len(arr)):
            current = min(arr[i], current + arr[i])
            min_sum = min(min_sum, current)
        return min_sum
    
    # 情况1:最大子数组不跨越边界
    max_kadane = kadane_max(nums)
    
    # 情况2:最大子数组跨越边界
    total_sum = sum(nums)
    min_kadane = kadane_min(nums)
    max_circular = total_sum - min_kadane
    
    # 特殊情况:所有元素都是负数
    if max_circular == 0:
        return max_kadane
    
    return max(max_kadane, max_circular)

7.3 最长递增子序列和

def maxSumIncreasingSubsequence(nums):
    """
    最大递增子序列和(不要求连续)
    """
    if not nums:
        return 0
    
    n = len(nums)
    dp = nums[:]
    
    for i in range(1, n):
        for j in range(i):
            if nums[j] < nums[i]:
                dp[i] = max(dp[i], dp[j] + nums[i])
    
    return max(dp)

8. 性能优化技巧

8.1 早期终止优化

def maxSubArray_optimized(nums):
    """
    带早期终止的优化版本
    """
    if not nums:
        return 0
    
    current_sum = nums[0]
    max_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)
        
        # 如果当前已经达到理论最大值,可以提前终止
        if max_sum == sum(x for x in nums if x > 0):
            break
    
    return max_sum

8.2 并行化处理

import multiprocessing as mp

def maxSubArray_parallel(nums, chunk_size=1000):
    """
    大数组的并行处理版本
    """
    if len(nums) <= chunk_size:
        return maxSubArray(nums)
    
    # 分块处理
    chunks = [nums[i:i+chunk_size] for i in range(0, len(nums), chunk_size)]
    
    with mp.Pool() as pool:
        chunk_results = pool.map(maxSubArray, chunks)
    
    # 处理块之间的连接部分
    # 这里需要更复杂的逻辑来处理跨块的最大子数组
    return max(chunk_results)  # 简化版本

8.3 内存优化

def maxSubArray_memory_efficient(nums):
    """
    内存高效版本,适用于超大数组
    """
    if not nums:
        return 0
    
    # 使用生成器避免一次性加载所有数据
    def process_stream(stream):
        current_sum = next(stream)
        max_sum = current_sum
        
        for num in stream:
            current_sum = max(num, current_sum + num)
            max_sum = max(max_sum, current_sum)
        
        return max_sum
    
    return process_stream(iter(nums))

9. 实际应用场景

9.1 股票交易

def maxProfit_stock(prices):
    """
    股票最大利润问题(转化为最大子数组和)
    """
    if len(prices) < 2:
        return 0
    
    # 将价格数组转换为差价数组
    diffs = [prices[i] - prices[i-1] for i in range(1, len(prices))]
    
    # 求最大子数组和
    return max(0, maxSubArray(diffs))

9.2 游戏得分系统

def maxGameScore(scores):
    """
    游戏中连续关卡的最大得分
    """
    max_score, start, end = maxSubArrayWithIndex(scores)
    
    return {
        'max_score': max_score,
        'best_streak_start': start,
        'best_streak_end': end,
        'streak_length': end - start + 1
    }

9.3 传感器数据分析

def analyzeSensorData(readings):
    """
    分析传感器数据中的异常模式
    """
    # 计算相邻读数的差值
    changes = [readings[i] - readings[i-1] for i in range(1, len(readings))]
    
    # 找到最大的正向变化区间
    max_increase = maxSubArray([x for x in changes if x >= 0] or [0])
    
    # 找到最大的负向变化区间
    max_decrease = abs(maxSubArray([-x for x in changes if x <= 0] or [0]))
    
    return {
        'max_continuous_increase': max_increase,
        'max_continuous_decrease': max_decrease
    }

10. 常见错误与调试

10.1 常见错误

错误1:忘记处理全负数数组

# 错误实现
def maxSubArray_wrong(nums):
    current_sum = 0  # 错误:应该初始化为nums[0]
    max_sum = 0      # 错误:全负数时会返回0
    
    for num in nums:
        current_sum = max(0, current_sum + num)  # 错误:会丢弃负数
        max_sum = max(max_sum, current_sum)
    
    return max_sum

错误2:边界条件处理不当

# 错误实现
def maxSubArray_wrong2(nums):
    if not nums:  # 缺少空数组检查
        pass      # 应该返回适当的值
    
    # 直接访问nums[0]可能导致IndexError
    current_sum = nums[0]

10.2 调试技巧

def maxSubArray_debug(nums):
    """
    带调试信息的版本
    """
    if not nums:
        print("Debug: 空数组输入")
        return 0
    
    current_sum = nums[0]
    max_sum = nums[0]
    debug_info = [(0, nums[0], nums[0])]  # (索引, 当前和, 最大和)
    
    for i in range(1, len(nums)):
        old_current = current_sum
        current_sum = max(nums[i], current_sum + nums[i])
        max_sum = max(max_sum, current_sum)
        
        debug_info.append((i, current_sum, max_sum))
        
        # 调试输出
        if current_sum == nums[i] and old_current + nums[i] != nums[i]:
            print(f"Debug: 位置{i}重新开始,丢弃前面的和{old_current}")
    
    print("Debug 轨迹:", debug_info)
    return max_sum

# 单元测试
def test_maxSubArray():
    test_cases = [
        ([-2,1,-3,4,-1,2,1,-5,4], 6),
        ([1], 1),
        ([5,4,-1,7,8], 23),
        ([-1], -1),
        ([-2,-1], -1)
    ]
    
    for nums, expected in test_cases:
        result = maxSubArray(nums)
        status = "✓" if result == expected else "✗"
        print(f"{status} 输入: {nums}, 期望: {expected}, 实际: {result}")

11. 面试要点总结

11.1 核心考点

  1. 算法理解:能否理解Kadane算法的核心思想
  2. 代码实现:能否正确实现并处理边界情况
  3. 复杂度分析:能否分析时间和空间复杂度
  4. 优化思路:能否提出不同的解法并比较优劣

11.2 面试回答框架

第一步:理解问题

  • 确认是连续子数组,不是子序列
  • 询问边界情况:空数组、全负数等
  • 确认返回值:是和还是子数组本身

第二步:分析解法

  • 暴力法:O(n²),简单但效率低
  • 动态规划:O(n)时间,O(n)空间
  • Kadane算法:O(n)时间,O(1)空间,最优解

第三步:编码实现

  • 选择Kadane算法实现
  • 注意边界情况处理
  • 添加必要的注释

第四步:测试验证

  • 正常情况测试
  • 边界情况测试
  • 复杂度分析

11.3 扩展问题准备

  • 如果要返回子数组的起始和结束位置怎么办?
  • 如果数组是环形的怎么处理?
  • 如果要求最大子数组乘积呢?
  • 能否用分治法解决?时间复杂度是多少?

11.4 面试加分点

  • 主动提及多种解法并比较
  • 考虑边界情况的处理
  • 分析算法的适用场景
  • 提及相关的变种问题
  • 讨论实际应用场景

12. 总结

最大子数组和问题是动态规划的经典应用,Kadane算法提供了优雅的O(n)时间、O(1)空间解决方案。

核心要点

  1. 算法本质:在每个位置做出最优决策(继续 vs 重新开始)
  2. 状态转移:current_sum = max(nums[i], current_sum + nums[i])
  3. 边界处理:空数组、单元素、全负数等情况
  4. 优化技巧:早期终止、并行处理、内存优化

学习建议

  1. 理解核心思想:掌握动态规划的状态转移
  2. 多种实现:练习不同的编程语言实现
  3. 变种练习:尝试相关的变种问题
  4. 实际应用:思考在实际项目中的应用场景

扩展学习

  • 二维数组的最大子矩阵和
  • 最大子数组乘积问题
  • 环形数组最大子数组和
  • 分治算法的深入理解
  • 动态规划的其他经典问题

这个问题不仅考查算法能力,更重要的是培养动态规划的思维方式,为解决更复杂的优化问题打下基础。

文章标签

算法数组动态规划LeetCode
全排列
上一篇

全排列

2025-11-17

三数之和
下一篇

三数之和

2025-11-17

冬眠

冬眠

博主

专注于技术、阅读与思考。在这里记录学习、思考与生活。

116
文章
3
分类
关注我
系列:数组算法

第 6 篇,共 8 篇

上一篇

三数之和

下一篇

全排列

文章目录

目录

  • 1. 问题描述
  • 2. 核心难点分析
  • 3. 解法分析
  • 4. 详细代码实现
  • 5. 复杂度分析
  • 6. 边界情况处理
  • 7. 相关变种问题
  • 8. 性能优化技巧
  • 9. 实际应用场景
  • 10. 常见错误与调试
  • 11. 面试要点总结
  • 12. 总结

相关文章

查看更多
最大子序列和

最大子序列和

2025-11-17 · 26 min read

一维数组:最长上升子序列

一维数组:最长上升子序列

2025-11-17 · 14 min read

最长递增子序列

最长递增子序列

2025-11-17 · 26 min read