Leetcode152. Maximum Product Subarray
Given an integer array nums, find the contiguous subarray within an array (containing at least one number) which has the largest product.
Example 1:
Input: [2,3,-2,4]
Output: 6
Explanation: [2,3] has the largest product 6.
Example 2:
Input: [-2,0,-1]
Output: 0
Explanation: The result cannot be 2, because [-2,-1] is not a subarray.
解法一 动态规划
dpMax[i]
表示以第 i
个元素的结尾的子数组的乘积最大的值。
- 当
nums[i] >= 0
并且dpMax[i-1] > 0
,dpMax[i] = dpMax[i-1] * nums[i]
- 当
nums[i] >= 0
并且dpMax[i-1] < 0
,此时如果和前边的数累乘的话,会变成负数,所以dpMax[i] = nums[i]
- 当
nums[i] < 0
,此时如果前边累乘结果是一个很大的负数,和当前负数累乘的话就会变成一个更大的数。所以我们还需要一个数组dpMin
来记录以第i
个元素的结尾的子数组的乘积最小的值。- 当
dpMin[i-1] < 0
,dpMax[i] = dpMin[i-1] * nums[i]
- 当
dpMin[i-1] >= 0
,dpMax[i] = nums[i]
- 当
所以状态转移方程为:dpMax[i] = max(dpMax[i-1] * nums[i], dpMin[i-1] * nums[i], nums[i]);
同理,dpMin[i] = min(dpMax[i-1] * nums[i], dpMin[i-1] * nums[i], nums[i]);
public int maxProduct(int[] nums) {
int n = nums.length;
if (n == 0) {
return 0;
}
int[] dpMax = new int[n];
dpMax[0] = nums[0];
int[] dpMin = new int[n];
dpMin[0] = nums[0];
int max = nums[0];
for (int i = 1; i < n; i++) {
dpMax[i] = Math.max(dpMin[i - 1] * nums[i], Math.max(dpMax[i - 1] * nums[i], nums[i]));
dpMin[i] = Math.min(dpMin[i - 1] * nums[i], Math.min(dpMax[i - 1] * nums[i], nums[i]));
max = Math.max(max, dpMax[i]);
}
return max;
}
再优化一下空间,利用一个变量表示dp[i-1]
public int maxProduct(int[] nums) {
int n = nums.length;
if (n == 0) {
return 0;
}
int dpMax = nums[0];
int dpMin = nums[0];
int max = nums[0];
for (int i = 1; i < n; i++) {
//更新 dpMin 的时候需要 dpMax 之前的信息,所以先保存起来
int preMax = dpMax;
dpMax = Math.max(dpMin * nums[i], Math.max(dpMax * nums[i], nums[i]));
dpMin = Math.min(dpMin * nums[i], Math.min(preMax * nums[i], nums[i]));
max = Math.max(max, dpMax);
}
return max;
}
解法二
如果给定的数组全部都是正数,那么子数组最大的乘积是把所有的数字相乘。
如果出现了偶数个负数,则也是把所有的数字相乘。
问题转化为奇数个负数时如何处理,即下面两种情况
① 不包含最后一个负数的子数组。
② 不包含第一个负数的子数组。
public int maxProduct(int[] nums) {
if (nums.length == 0) {
return 0;
}
int max = 1;
int res = nums[0];
//包含了所有数相乘的情况
for (int i = 0; i < nums.length; i++) {//奇数个负数的情况一,正向遍历
max *= nums[i];
res = Math.max(res, max);
}
max = 1;
for (int i = nums.length - 1; i >= 0; i--) {//奇数个负数的情况二,反向遍历
max *= nums[i];
res = Math.max(res, max);
}
return res;
}
但是如果遍历中出现了元素0,则到 0 的位置之后 max 就一直变成 0 了。
解决方法:把数组看成几个都不含有 0 的子数组。即在遇到零的时候,把 max 再初始化为 1 。
public int maxProduct(int[] nums) {
if (nums.length == 0) {
return 0;
}
int max = 1;
int res = nums[0];
for (int i = 0; i < nums.length; i++) {
max *= nums[i];
res = Math.max(res, max);
if (max == 0) {
max = 1;
}
}
max = 1;
for (int i = nums.length - 1; i >= 0; i--) {
max *= nums[i];
res = Math.max(res, max);
if (max == 0) {
max = 1;
}
}
return res;
}