【动态规划-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)
相关推荐
信奥卷王1 小时前
[GESP202512 六级] 路径覆盖
数据结构·算法
INGNIGHT1 小时前
1861 · 老鼠跳跃(坐标型dp)
数据结构·c++
Lazionr1 小时前
手撕 unordered_set 和 unordered_map,剖析哈希表底层实现
c++·算法
0+1112 小时前
算法 --归并排序
数据结构·算法·排序算法
Run_Teenage2 小时前
算法:树的重心、树的直径
算法
专注仿真2 小时前
高代价计算后置:探测、交战、毁伤三条链路的统一设计哲学
算法·探测·毁伤
xie0510_2 小时前
Any类简要实现
开发语言·c++·算法
字节跳动的猫2 小时前
LikeShop 种草社区模块二开:笔记发布、话题广场与商品挂载实现
数据结构·数据库
在所不辞兄2 小时前
为什么PINN非常适合求解有限元模型
人工智能·深度学习·神经网络·算法·机器学习