← 返回题目列表

最大乘积子数组为什么要同时维护最大值和最小值?

高频 中等 第 14 / 33 题 更新于 2026/07/30
动态规划子数组最大乘积状态压缩

简化版

最大乘积子数组需要同时维护以当前位置结尾的最大乘积 maxEnd 和最小乘积 minEnd。因为遇到负数时,之前的最小乘积乘上负数可能变成最大值;遇到 0 时状态会自然重置。

详细版

普通最大子数组和只需要维护一个“以当前位置结尾的最大和”,但乘积有符号翻转问题。设当前数为 x,以当前位置结尾的最大乘积可能来自 xmaxEnd * xminEnd * x;最小乘积同理也要取这三者的最小值。

每一步更新全局答案 ans = max(ans, maxEnd)。初始值取第一个元素,遍历从第二个元素开始。时间复杂度 O(n),空间复杂度 O(1)

完整版教学

一、为什么最大和的套路不够用

最大子数组和中,如果前缀和为负,继续带着它只会拖累后面。但乘积不一样,负数乘负数会变成正数。

nums = [2, 3, -2, 4]
最大乘积是 2 * 3 = 6

nums = [-2, 3, -4]
最大乘积是 -2 * 3 * -4 = 24

第二个例子里,前面的负乘积不能随便丢,因为它可能被后面的负数翻成最大正数。

二、状态必须成对维护

定义:

maxEnd:以当前位置结尾的最大乘积
minEnd:以当前位置结尾的最小乘积

当前数 x 到来时,有三种选择:

来源含义
x从当前位置重新开始
maxEnd * x接在之前最大乘积后面
minEnd * x接在之前最小乘积后面

新的最大值取三者最大,新的最小值取三者最小。

记忆钩子:乘积 DP 里“最坏的负数”可能在下一个负数面前变成最好。

三、转移公式

设旧状态为 oldMaxoldMin,当前数为 x

newMax = max(x, oldMax * x, oldMin * x)
newMin = min(x, oldMax * x, oldMin * x)
ans = max(ans, newMax)

注意必须用旧的 maxEndminEnd 同时计算,不能先更新 maxEnd 后再拿新值算 minEnd

四、0 如何处理

0 会把任何乘积截断。公式里把 x 本身作为候选,已经自然处理了 0。

nums = [-2, 0, -1]
到 0 时:newMax = 0, newMin = 0
到 -1 时:可从 -1 重新开始
最大答案仍是 0

所以不一定要手写“遇到 0 重置”,统一公式更安全。

五、代码模板

int maxProduct(int[] nums) {
    int maxEnd = nums[0];
    int minEnd = nums[0];
    int ans = nums[0];

    for (int i = 1; i < nums.length; i++) {
        int x = nums[i];
        int oldMax = maxEnd;
        int oldMin = minEnd;

        maxEnd = Math.max(x, Math.max(oldMax * x, oldMin * x));
        minEnd = Math.min(x, Math.min(oldMax * x, oldMin * x));
        ans = Math.max(ans, maxEnd);
    }
    return ans;
}

如果使用语言存在整数溢出风险,要根据题目范围决定是否使用更大的数值类型。

六、用样例走一遍

[-2, 3, -4] 为例:

xmaxEndminEndans
-2-2-2-2
33-63
-424-1224

关键就在最后一步:oldMin * -4 = 24 成为最大乘积。

七、常见误区与追问

  • 误区:只维护最大乘积。 遇到负数时,最小乘积可能翻转成最大乘积。
  • 误区:更新 minEnd 时使用已经更新后的 maxEnd。 两个新状态都必须来自旧状态。
  • 误区:遇到 0 后直接跳过。 0 本身可能是最大答案,尤其数组全为负且被 0 分隔时。
  • 追问:为什么不是滑动窗口? 负数和 0 会破坏单调性,窗口扩张收缩没有稳定规则。
  • 追问:复杂度是多少? 单次遍历,时间 O(n),空间 O(1)
  • 追问:和最大子数组和有什么区别? 和只需要最大前缀状态,乘积需要最大和最小两个状态。

八、加强记忆

最大乘积子数组的核心是“负负得正”。普通线性 DP 只保留最大状态会丢掉未来翻盘的负数,所以必须同时保留 maxEndminEnd。每次面对当前数,都比较“重新开始、接最大、接最小”三种选择;0 不用特殊恐惧,公式会把它当作重新开始点。面试时用 [-2,3,-4] 解释最小值翻成最大值,基本就能讲清楚这题。