题目:给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
示例 1:
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
题目分析:动态规划
我们先定义一个数组 dpMaxdpMaxdpMax,用 dpMax[i]dpMax[i]dpMax[i] 表示以第 iii 个元素的结尾的子数组,乘积最大的值,也就是这个数组必须包含第 iii 个元素。
那么 dpMax[i]dpMax[i]dpMax[i] 的取值情况有以下几种:
-
当 nums[i]≥0nums[i] \ge 0nums[i]≥0 并且 dpMax[i−1]≥0dpMax[i - 1] \ge 0dpMax[i−1]≥0,dpMax[i]=dpMax[i−1]∗nums[i]dpMax[i] = dpMax[i - 1] * nums[i]dpMax[i]=dpMax[i−1]∗nums[i]。
-
当 nums[i]≥0nums[i] \ge 0nums[i]≥0 并且 dpMax[i−1]<0dpMax[i - 1] < 0dpMax[i−1]<0,此时如果和前边的数累乘的话,结果会负得更多(即值越小),所以直接将nums[i]nums[i]nums[i] 赋值给当前的结果, dpMax[i]=nums[i]dpMax[i] = nums[i]dpMax[i]=nums[i] 。
-
当 nums[i]<0nums[i] < 0nums[i]<0,如果前面累乘结果(dpMax[i−1]dpMax[i - 1]dpMax[i−1]) 是一个负很多的数(即很小的负数), 和当前负数累乘的话(dpMax[i−1]∗nums[i]dpMax[i - 1] * nums[i]dpMax[i−1]∗nums[i])就会变成一个更大的正数。因此,还需要一个数组 dpMindpMindpMin 来记录以第 iii 个元素的结尾的子数组,乘积最小的值。
当 dpMin[i−1]<0dpMin[i - 1] < 0dpMin[i−1]<0,dpMax[i]=dpMin[i−1]∗nums[i]dpMax[i] = dpMin[i - 1] * nums[i]dpMax[i]=dpMin[i−1]∗nums[i];
当 dpMin[i−1]≥0dpMin[i - 1] \ge 0dpMin[i−1]≥0,dpMax[i]=nums[i]dpMax[i] = nums[i]dpMax[i]=nums[i]。
dpMindpMindpMin的求解其实和上边求 dpMaxdpMaxdpMax 的过程其实是一样的。
我们注意到上边 dpMax[i]dpMax[i]dpMax[i] 的取值无非就是三种,dpMax[i−1]∗nums[i]dpMax[i - 1] * nums[i]dpMax[i−1]∗nums[i]、dpMin[i−1]∗nums[i]dpMin[i - 1] * nums[i]dpMin[i−1]∗nums[i]以及 nums[i]nums[i]nums[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])dpMax[i]=max(dpMax[i−1]∗nums[i],dpMin[i−1]∗nums[i],nums[i]) ;
求 dpMindpMindpMin 同理:
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])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 等平台只要我们完成一个函数即可,本人初出茅庐,为了巩固基本知识,故自己补充了部分代码,用于练手。本代码也许存在漏洞,望高手赐教。感谢!