Leetcode-100乘积最大子数组

乘积最大子数组

题目概括

给定一个整数数组 nums,找出数组中乘积最大的非空连续子数组(至少包含一个数字),返回其对应乘积。要求结果在 32 位整数范围内。

示例
输入:nums = [2,3,-2,4]
输出:6
解释:子数组 [2,3] 的乘积是 6,其余子数组乘积均小于 6。


算法思想

动态规划(双状态维护)

核心问题:负数的存在可能使最小值翻转为最大值(如 -5 * -3 = 15)。

  • 维护两个状态:
    • f_max:以当前元素结尾的子数组的最大乘积
    • f_min:以当前元素结尾的子数组的最小乘积
  • 对每个元素 x,计算三种可能性:
    1. 延续前序子数组(f_max * x
    2. 前序最小乘积可能翻转为最大(f_min * x
    3. 以当前元素重新开始(x

关键点

  • 遇到负数时,交换 f_maxf_min 的值(或通过同时计算实现等效效果)
  • 始终维护全局最大值 ans

算法步骤

  1. 初始化

    • ans 设为负无穷(处理全负数或含零的情况)
    • f_max = f_min = 1(空乘积初始值)
  2. 遍历数组

    • 对每个元素 x
      • 计算候选值:temp_max = f_max * x, temp_min = f_min * x
      • 更新当前状态:
        f_max = max(temp_max, temp_min, x)  # 三者取最大
        f_min = min(temp_max, temp_min, x)  # 三者取最小
        
      • 更新全局最大值:ans = max(ans, f_max)
  3. 返回结果ans


具体代码

class Solution:
    def maxProduct(self, nums: List[int]) -> int:
        ans = -float('inf')  # 初始化全局最大值
        f_max = f_min = 1    # 空乘积初始值
        
        for x in nums:
            # 同时计算候选值,避免顺序影响
            candidates = (f_max * x, f_min * x, x)
            f_max = max(candidates)  # 更新当前最大乘积
            f_min = min(candidates)  # 更新当前最小乘积
            
            ans = max(ans, f_max)     # 维护全局最大值
        
        return ans

时间复杂度

  • 时间复杂度: O ( n ) O(n) O(n)
    仅需一次线性遍历,每个元素处理时间为 O ( 1 ) O(1) O(1)

  • 空间复杂度: O ( 1 ) O(1) O(1)
    仅使用常数级别的额外空间(f_maxf_minans)。

示例解析

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

遍历过程:

xf_maxf_minans
2222
3636
-2-2-66
44-86

结果: 6

案例2:nums = [-2, 0, -1]

遍历过程:

xf_maxf_minans
-2-2-2-2
0000
-10-10

结果: 0

为什么需要维护最小值?

当遇到负数时,之前的 f_min(可能为负数)与之相乘会变成正数,可能成为新的最大值。例如:

nums = [-3, -4]

处理第一个 -3:f_max = -3, f_min = -3

处理第二个 -4:

  • temp_max = (-3) * (-4) = 12
  • temp_min = (-3) * (-4) = 12
  • f_max = max(12, 12, -4) = 12

最终结果为12


为什么需要与自身比较(x)?

在动态规划解法中,f_maxf_min 表示以当前元素 x 结尾的子数组的最大和最小乘积。在状态转移时,我们需要考虑以下三种可能性:

  1. 延续前序子数组
    x 与前序子数组的乘积相乘(f_max * xf_min * x)。
  2. x 重新开始
    丢弃前面的所有元素,单独以 x 作为新的子数组(即子数组只包含 x)。

为何必须考虑 x 自身?

场景1:前序乘积为负数,当前元素为正数

  • 示例nums = [-2, 3]
    • 处理到 3 时,前序最大乘积是 -2
    • 若延续前序子数组:乘积为 -2 * 3 = -6
    • 若单独以 3 开始:乘积为 3
    • 最终选择 3,此时必须将 x 纳入比较。

场景2:前序乘积为0,当前元素较大

  • 示例nums = [0, 5]
    • 处理到 5 时,前序乘积为 0
    • 延续前序乘积:0 * 5 = 0
    • 单独以 5 开始:乘积为 5
    • 最终选择 5

场景3:当前元素是唯一选项

  • 示例nums = [-3]
    • 由于子数组必须非空,唯一选择是 -3
    • 必须将 x 自身作为候选值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值