题目:给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
示例 1:
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
题目分析:动态规划
我们先定义一个数组 d p M a x dpMax dpMax,用 d p M a x [ i ] dpMax[i] dpMax[i] 表示以第 i i i 个元素的结尾的子数组,乘积最大的值,也就是这个数组必须包含第 i i i 个元素。
那么 d p M a x [ i ] dpMax[i] dpMax[i] 的取值情况有以下几种:
-
当 n u m s [ i ] ≥ 0 nums[i] \ge 0 nums[i]≥0 并且 d p M a x [ i − 1 ] ≥ 0 dpMax[i - 1] \ge 0 dpMax[i−1]≥0, d p M a x [ i ] = d p M a x [ i − 1 ] ∗ n u m s [ i ] dpMax[i] = dpMax[i - 1] * nums[i] dpMax[i]=dpMax[i−1]∗nums[i]。
-
当 n u m s [ i ] ≥ 0 nums[i] \ge 0 nums[i]≥0 并且 d p M a x [ i − 1 ] < 0 dpMax[i - 1] < 0 dpMax[i−1]<0,此时如果和前边的数累乘的话,结果会负得更多(即值越小),所以直接将 n u m s [ i ] nums[i] nums[i] 赋值给当前的结果, d p M a x [ i ] = n u m s [ i ] dpMax[i] = nums[i] dpMax[i]=nums[i] 。
-
当 n u m s [ i ] < 0 nums[i] < 0 nums[i]<0,如果前面累乘结果( d p M a x [ i − 1 ] dpMax[i - 1] dpMax[i−1]) 是一个负很多的数(即很小的负数), 和当前负数累乘的话( d p M a x [ i − 1 ] ∗ n u m s [ i ] dpMax[i - 1] * nums[i] dpMax[i−1]∗nums[i])就会变成一个更大的正数。因此,还需要一个数组 d p M i n dpMin dpMin 来记录以第 i i i 个元素的结尾的子数组,乘积最小的值。
当 d p M i n [ i − 1 ] < 0 dpMin[i - 1] < 0 dpMin[i−1]<0, d p M a x [ i ] = d p M i n [ i − 1 ] ∗ n u m s [ i ] dpMax[i] = dpMin[i - 1] * nums[i] dpMax[i]=dpMin[i−1]∗nums[i];
当 d p M i n [ i − 1 ] ≥ 0 dpMin[i - 1] \ge 0 dpMin[i−1]≥0, d p M a x [ i ] = n u m s [ i ] dpMax[i] = nums[i] dpMax[i]=nums[i]。
d p M i n dpMin dpMin的求解其实和上边求 d p M a x dpMax dpMax 的过程其实是一样的。
我们注意到上边 d p M a x [ i ] dpMax[i] dpMax[i] 的取值无非就是三种, d p M a x [ i − 1 ] ∗ n u m s [ i ] dpMax[i - 1] * nums[i] dpMax[i−1]∗nums[i]、 d p M i n [ i − 1 ] ∗ n u m s [ i ] dpMin[i - 1] * nums[i] dpMin[i−1]∗nums[i]以及 n u m s [ i ] nums[i] nums[i]。
实际计算的时候,只需要从三个取值中选一个最大的即可: d p M a x [ i ] = max ( d p M a x [ i − 1 ] ∗ n u m s [ i ] , d p M i n [ i − 1 ] ∗ n u m s [ i ] , n u m s [ i ] ) dpMax[i] = \max (dpMax[i - 1] * nums[i],dpMin[i - 1] * nums[i],nums[i]) dpMax[i]=max(dpMax[i−1]∗nums[i],dpMin[i−1]∗nums[i],nums[i]) ;
求
d
p
M
i
n
dpMin
dpMin 同理:
d
p
M
i
n
[
i
]
=
min
(
d
p
M
a
x
[
i
−
1
]
∗
n
u
m
s
[
i
]
,
d
p
M
i
n
[
i
−
1
]
∗
n
u
m
s
[
i
]
,
n
u
m
s
[
i
]
)
dpMin[i] = \min (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])
Java代码:
/**
* 【题目】: 乘积最大子数组
* 给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子
* 数组中至少包含一个数字),并返回该子数组所对应的乘积。
*
* 示例 1:
* 输入: [2,3,-2,4]
* 输出: 6
* 解释: 子数组 [2,3] 有最大乘积 6。
*
* 示例 2:
* 输入: [-2,0,-1]
* 输出: 0
* 解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。给定一个单链表,编写
一个函数返回该链表的中间点。
*/
public class leetcode_152 {
/**
* 动态规划求解 乘积最大的子数组
* @param nums
* @return
*/
public static 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(dpMax[i-1]*nums[i], Math.max(dpMin[i-1]*nums[i], nums[i]));
dpMin[i] = Math.min(dpMax[i-1]*nums[i], Math.min(dpMin[i-1]*nums[i], nums[i]));
max = Math.max(max, dpMax[i]);
}
return max;
}
public static void main(String[] args) {
int[] array = {1,4,-6,7,-2,-5, 3,-8,9};
System.out.println(maxProduct(array));
}
}
【注】
(1):leetcode 等平台只要我们完成一个函数即可,本人初出茅庐,为了巩固基本知识,故自己补充了部分代码,用于练手。本代码也许存在漏洞,望高手赐教。感谢!