【动态规划-8】152.乘积最大子数组

题目描述:

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

子数组 是数组中连续的 非空 元素序列。

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

请注意,一个只包含一个元素的数组的乘积是这个元素的值。

示例 1:

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

示例 2:

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

解题思路:

方法一:动态规划

核心思路:

因为存在负数,最小值乘以负数可能变成最大值。

所以需要同时维护:

  • maxProd:以当前元素结尾的最大乘积

  • minProd:以当前元素结尾的最小乘积

状态转移:

对于每个 nums[i]:

复制代码
newMax = max(nums[i], maxProd * nums[i], minProd * nums[i])
newMin = min(nums[i], maxProd * nums[i], minProd * nums[i])

为什么考虑 nums[i] 本身?

因为如果之前的乘积为 0 或负数,不如从当前元素重新开始。

具体过程示例:

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

i numsi maxProd minProd 说明
0 2 2 2 初始化
1 3 6 3 2*3=6, 2*3=6
2 -2 -2 -12 max(-2, 6*-2=-12, 3*-2=-6) = -2
3 4 4 -48 max(4, -2*4=-8, -12*4=-48) = 4

最大乘积 = 6 ✅

nums = [-2,0,-1]

i numsi maxProd minProd 说明
0 -2 -2 -2 初始化
1 0 0 0 max(0, -2*0=0, -2*0=0) = 0
2 -1 0 -1 max(-1, 0*-1=0, 0*-1=0) = 0

最大乘积 = 0 ✅

代码实现:

cpp 复制代码
class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int maxProd = nums[0];
        int minProd = nums[0];
        int result = nums[0];
        
        for (int i = 1; i < nums.size(); i++) {
            int num = nums[i];
            
            // 先保存 maxProd,因为下面要更新
            int tempMax = maxProd;
            
            maxProd = max({num, maxProd * num, minProd * num});
            minProd = min({num, tempMax * num, minProd * num});
            
            result = max(result, maxProd);
        }
        
        return result;
    }
};

更简洁的写法:

cpp 复制代码
class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int maxProd = 1, minProd = 1;
        int result = INT_MIN;
        
        for (int num : nums) {
            if (num < 0) swap(maxProd, minProd);
            
            maxProd = max(num, maxProd * num);
            minProd = min(num, minProd * num);
            
            result = max(result, maxProd);
        }
        
        return result;
    }
};

交换技巧:遇到负数时,最大值和最小值互换,因为负数会让大的变小、小的变大。

复杂度分析:

维度 复杂度 说明
时间复杂度 O(n) 一次遍历
空间复杂度 O(1) 只用常数个变量

关键细节:

1. 为什么需要同时维护最小值和最大值?

因为负数乘以负数会变成正数。

nums = [-2, 3, -4]

  • -2 * 3 = -6(最小值)

  • -6 * -4 = 24(最大值)

如果只维护最大值,会错过这个情况。

2. 为什么考虑 nums[i] 本身?

如果之前的乘积是 0 或负数,不如从当前元素重新开始。

nums = [0, 2]

  • 0 * 2 = 0

  • 但从 2 重新开始,乘积是 2

3. 为什么用 tempMax 保存旧值?

因为更新 maxProd 后,minProd 的计算需要用到旧的 maxProd。如果不保存,maxProd 已经被修改,minProd 会算错。

4. 和「最大子数组和」的区别
题目 区别
53. 最大子数组和 加法,只需维护最大值
152. 乘积最大子数组 乘法,需要维护最大值和最小值

总结:

要点 说明
核心思想 同时维护最大乘积和最小乘积
状态转移 maxProd = max(num, maxProd*num, minProd*num)
关键技巧 负数会让最大最小互换
时间复杂度 O(n)
空间复杂度 O(1)
相关推荐
傲世仙尊17 分钟前
继承-类设计层次复用切片转换与菱形继承的死结
c++
架构师伯子明19 分钟前
FFmpeg C++学习(一):编译和demo
开发语言·c++·学习
LabVIEW开发28 分钟前
CTM-3 红外测温仪 + LabVIEW 温度怎么测?
算法·labview·测试·labview程序
纪念 22943 分钟前
C++算法(六)
开发语言·c++·算法
ShineWinsu1 小时前
对于Qt:QCheckBox常见属性的详细解析
c++·qt·面试·笔试·gui·qcheckbox·多选框
Dumbness2 小时前
UIA 读微信群消息没有 id,怎么判断哪条是新的:后缀对齐在翻历史和撤回时整屏重报
python·算法
taka2 小时前
从 24x 到 141x:我用纯 Python 零依赖项目,扒开了 GPU 加速的性能黑洞
算法
AI的探索之旅2 小时前
97 个 OpenCV 实例(三十七):二维码识别,一张图四个码的陷阱
c++·opencv·计算机视觉
UIU1142 小时前
P16237 [蓝桥杯 2026 省 B] 应急布线
数据结构·c++·算法·蓝桥杯
raindayinrain2 小时前
c++设计模式
开发语言·c++·设计模式