题目描述
给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续 子数组 (该子数组中至少包含一个数字),并返回该子数组所对应的乘积。 测试用例的答案是一个 32-位 整数。
解题思路
使用了两个动态规划数组 f
和 g
来分别跟踪到当前位置为止,乘积最大和最小的子数组乘积。这是基于以下观察:
-
最大乘积(
f
数组):在遍历数组时,对于每个位置i
,最大乘积要么是当前元素nums[i]
本身(如果前面的元素都是负数或者乘积小于当前元素),要么是前面位置的最大乘积f[i-1]
乘以当前元素nums[i]
(如果前面的乘积是正数或者0)。但是,当遇到负数时,我们需要考虑前一个位置的最小乘积g[i-1]
乘以当前负数nums[i]
是否能产生新的最大乘积(因为负数乘以负数会变成正数)。 -
最小乘积(
g
数组):与最大乘积类似,最小乘积的更新也考虑了当前元素、前一个位置的最小乘积乘以当前元素,以及前一个位置的最大乘积乘以当前元素(当当前元素为负数时)。这是因为,负数乘以正数(前一个位置的最大乘积)会得到一个更小的负数,这可能是新的最小乘积。
上述的动态规划思路与此前的最大子数组和一题相似。
在遍历过程中,f
和 g
数组被逐步构建,并且我们始终保持 f
数组中的最大值作为当前已知的最大乘积。遍历完成后,f
数组中的最大值即为所求的最大乘积子数组的乘积。
代码示例
class Solution {
public:
int maxProduct(vector<int>& nums) {
int n = nums.size();
vector<int> f(n, 0); // 乘积最大的子数组
vector<int> g(n, 0); // 乘积最小的子数组
f[0] = g[0] = nums[0];
for (int i = 1; i < n; i++) {
if (nums[i] > 0) {
f[i] = max(nums[i] * f[i - 1], nums[i]);
g[i] = min(nums[i], nums[i] * g[i - 1]);
} else {
f[i] = max(nums[i], nums[i] * g[i - 1]);
g[i] = min(nums[i], nums[i] * f[i - 1]);
}
}
int ret = f[0];
for (int i = 1; i < n; i++) {
ret = max(ret, f[i]);
}
return ret;
}
};
以下是代码的主要步骤:
- 初始化
f[0]
和g[0]
为nums[0]
,因为第一个元素既是最大乘积也是最小乘积的起点。 - 遍历数组
nums
,从第二个元素开始:- 如果当前元素
nums[i]
是正数,则最大乘积f[i]
可以是nums[i]
或者nums[i] * f[i-1]
(取决于哪个更大),最小乘积g[i]
可以是nums[i]
或者nums[i] * g[i-1]
(取决于哪个更小)。 - 如果当前元素
nums[i]
是负数,则最大乘积f[i]
可以是nums[i]
或者nums[i] * g[i-1]
(因为负数乘以负数变正数,可能成为新的最大乘积),最小乘积g[i]
可以是nums[i]
或者nums[i] * f[i-1]
(因为负数乘以正数变得更小)。
- 如果当前元素
- 遍历完成后,
f
数组中的最大值即为所求的最大乘积。