152. 乘积最大子数组

152. 乘积最大子数组

中等

给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续 子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。

测试用例的答案是一个 32-位 整数。

示例 1:

复制代码
输入: nums = [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。

示例 2:

复制代码
输入: nums = [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

提示:

  • 1 <= nums.length <= 2 * 104
  • -10 <= nums[i] <= 10
  • nums 的任何子数组的乘积都 保证 是一个 32-位 整数

📝 核心笔记:乘积最大子数组 (Maximum Product Subarray)

1. 核心思想 (一句话总结)

"双面间谍策略:同时维护'最大值'和'最小值',因为当前的'最小值'(负数)乘以一个负数,可能立马翻身变成'最大值'。"

  • 状态定义
    • fMax:以当前位置结尾的 最大 乘积。
    • fMin:以当前位置结尾的 最小 乘积(为了等待下一个负数来实现逆袭)。
  • 转移方程:由于负数的存在,最大值可能来自:
    1. fMax * x(正数 正数)
    2. fMin * x(负数 负数 变成大正数)
    3. x 本身(前面的积累太烂了,不如从头开始,相当于 Kadane 中的 restart)。
2. 算法流程 (DP 滚动数组)
  1. 初始化 (Init)
    • ans 设为 MIN_VALUE(或 nums[0]),因为结果可能是负数。
    • fMaxfMin 初始化为 1(作为乘法的单位元,或者配合循环内的逻辑,第一次遇到 x 时会自动变成 x)。
  1. 遍历 (Loop)
    • 计算三个候选值:fMax * x, fMin * x, x
    • 暂存 (Temp) :因为 fMax 会先被更新,所以计算 fMin 时需要用到 更新前fMax,必须用变量 mx 暂存。
    • 更新 (Update)
      • fMax 取三个候选中的最大值。
      • fMin 取三个候选中的最小值。
  1. 记录 (Record) :每次循环更新全局最大值 ans
🔍 代码回忆清单
复制代码
// 题目:LC 152. Maximum Product Subarray
class Solution {
    public int maxProduct(int[] nums) {
        int ans = Integer.MIN_VALUE; // 注意:结果可能是负数,不能设为 0
        int fMax = 1;
        int fMin = 1;
        
        for (int x : nums) {
            // 1. 暂存旧的 fMax
            // 因为下面马上要更新 fMax,而计算 fMin 需要用到旧的 fMax
            int mx = fMax; 
            
            // 2. 核心转移:在 (最大积*x, 最小积*x, x本身) 中找最大值
            // 包含 x 意味着:如果前面的积是 0 或很小的负数,不如从当前 x 重新开始
            fMax = Math.max(Math.max(fMax * x, fMin * x), x);
            
            // 3. 维护最小值:为了应对可能的负负得正
            // 注意这里用的是 mx (旧的 fMax)
            fMin = Math.min(Math.min(mx * x, fMin * x), x);
            
            // 4. 更新全局答案
            ans = Math.max(ans, fMax);
        }
        return ans;
    }
}
⚡ 快速复习 CheckList (易错点)
  • \] **为什么要维护** **fMin****?**

    • 如果输入是 [-2, 3, -4]
    • 走到 3 时,fMax 是 3,fMin 是 -6。
    • 走到 -4 时,3 * -4 = -12,但 -6 * -4 = 24。如果没有维护 fMin,就会漏掉 24 这个解。
  • \] **为什么要暂存** **mx****?**

    • 在计算 fMin 的时候,公式里需要 old_fMax * x。但上一行代码已经把 fMax 更新了。如果不暂存,就会用新的 fMax 去算 fMin,导致逻辑错误。
  • \] **fMax****和** **fMin****初始值设为 1 安全吗?**

    • 对于这种写法是安全的。
    • 假设数组是 [-2, ...]。第一轮循环:max(1*-2, 1*-2, -2) 结果是 -2。
    • 它利用了 Math.max(..., x) 这一项,确保了第一个元素会被正确处理(相当于从第一个元素强制 Restart)。
🖼️ 数字演练

nums = [2, 3, -2, 4]

  1. Init : ans = MIN, fMax = 1, fMin = 1.
  2. x = 2:
    • mx = 1.
    • fMax = max(2, 2, 2) = 2.
    • fMin = min(2, 2, 2) = 2.
    • ans = 2.
  1. x = 3:
    • mx = 2.
    • fMax = max(6, 6, 3) = 6. (延续前面的)
    • fMin = min(6, 6, 3) = 3.
    • ans = 6.
  1. x = -2 (关键转折):
    • mx = 6.
    • fMax = max(-12, -6, -2) = -2. (最大值变成了负数,或者选择重开)
    • fMin = min(-12, -6, -2) = -12. (埋下伏笔,保留了一个很大的负数)
    • ans = 6.
  1. x = 4:
    • mx = -2.
    • fMax = max(-8, -48, 4) = 4. (因为之前的 fMin 乘 4 还是负数,所以不如从 4 重开)
    • fMin = min(-8, -48, 4) = -48.
    • ans = 6.
  1. Result: 6.

(注:如果数组是 [2, 3, -2, -4]**,最后一步 fMax**会变成 max(-2-4, -12*-4, -4) = 48**,体现* fMin**的作用)

相关推荐
weixin_649555672 小时前
C语言程序设计第四版(何钦铭、颜晖)第八章之字符串压缩
c语言·数据结构·算法
单车少年ing2 小时前
一个编码BUG
算法·bug
努力中的编程者2 小时前
哈希表(C语言底层实现)
c语言·数据结构·c++·算法·哈希算法·散列表
CoovallyAIHub2 小时前
ICLR 2026 | MRAD:不拟合直接查表,零样本工业缺陷检测新范式,16 数据集均值最优
深度学习·算法·计算机视觉
摆烂小白敲代码2 小时前
【数据结构与算法】汉诺塔问题(C++)
c语言·开发语言·数据结构·c++·算法·hanoi·汉诺塔问题
Trouvaille ~2 小时前
【递归、搜索与回溯】专题(八):记忆化搜索——从暴力递归到动态规划的桥梁
c++·算法·leetcode·青少年编程·面试·蓝桥杯·动态规划
Sincerelyplz2 小时前
【LeetForge】我用AI写了一个 LeetCode 刷题自动追踪工具,从此告别手动打卡
leetcode·cursor
Pu_Nine_92 小时前
深入理解 ES6 Map 数据结构:从理论到实战应用
前端·javascript·数据结构·es6