152.乘积最大子数组
给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
测试用例的答案是一个 32-位 整数。
子数组 是数组的连续子序列。
示例 1:
输入: nums = [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: nums = [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
解题思路:
凡是涉及到子序列问题,应该第一时间想到使用动态规划。
我们还是正常使用动归五部曲:
1、确定dp数组(dp table)以及下标的含义
我们用 dp[i] 来表示第以第 i 个结尾的最大连续子序列的乘积。
2、确定递推公式
dp[i] = dp[i -1] * num[i];为什么我们能推导出这个公式?你想一下这里是连续子序列的乘积啊,注意我们 dp[i] 的定义,以第 i 个结尾的最大连续子序列的乘积,那么我们当前 i 的最大乘积 dp[i] = 前一个 * 当前值,举例,1,2,3,4.比如我们当前值是 3 ,那么它的前一个是 12 当前值 是 3 ,12* 3 =6,ok,懂了?但是你别忽略了,我们这里有负数的呀,我们需要的是乘积,负负是得正的,而且负数越小乘积越大,所有我们需要把正负分开来处理。
那么就是
maxF[i] = Math.max(maxF[i-1] * nums[i],Math.max(nums[i],minF[i-1] * nums[i]));
minF[i] = Math.min(minF[i-1] * nums[i],Math.min(nums[i],maxF[i-1] * nums[i]));
有人好奇为什么公式后面会有max(nums[i],minF[i-1] * nums[i]) /Math.min(nums[i],maxF[i-1] * nums[i])
负负得正,是不是有可能比我们 正正相乘要大,所以也需要从中间求出最大值,所以才有了Math.max(nums[i],minF[i-1] * nums[i])
,下面因为我们需要维护最小值,同理出现了min(nums[i],maxF[i-1] * nums[i])
。
ok到这了,那么我们是不是需要维护两个 dp 数组了。
3、dp 数组初始化
从递归公式我们可以看出 dp[i] 依赖与 dp[i-1] 所以 dp[0] 应该是多少呢?
根据 dp[i] 的定义,很明显 dp[0] 应为 nums[0] 即dp[0] = nums[0]以此类推,其实就是默认三个数组都一样,maxF、minF、num
4、确定遍历顺序
递推公式中 dp[i] 依赖于 dp[i - 1 ]的状态,需要从前向后遍历。
5、举例推导dp数组
在推导公式中已经说明了,你也可以写出代码,debug 推导。看哪里有误。
代码:
public static int maxProduct(int[] nums) {
//dp[i] 来表示第以第 i 个结尾的最大连续子序列的乘积。
int len = nums.length;
int[] maxF = new int[len];
int[] minF = new int[len];
//初始化dp数组
System.arraycopy(nums, 0, maxF, 0, len);
System.arraycopy(nums, 0, minF, 0, len);
//遍历顺序
for (int i = 1; i < len; i++) {
//递推公式
maxF[i] = Math.max(maxF[i-1] * nums[i],Math.max(nums[i],minF[i-1] * nums[i]));
minF[i] = Math.min(minF[i-1] * nums[i],Math.min(nums[i],maxF[i-1] * nums[i]));
}
//推导结果
int res = maxF[0];
for (int i = 1; i < len; i++) {
res = Math.max(res,maxF[i]);
}
return res;
}
时间复杂度:o(n)
空间复杂度:o(n)
官方优化后:
public int maxProduct(int[] nums) {
int maxF = nums[0], minF = nums[0], ans = nums[0];
int length = nums.length;
for (int i = 1; i < length; ++i) {
int mx = maxF, mn = minF;
maxF = Math.max(mx * nums[i], Math.max(nums[i], mn * nums[i]));
minF = Math.min(mn * nums[i], Math.min(nums[i], mx * nums[i]));
ans = Math.max(maxF, ans);
}
return ans;
}
时间复杂度:o(n)
空间复杂度:o(1)