题目描述:
给你一个整数数组
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) |