题目
给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
测试用例的答案是一个 32-位 整数。
子数组 是数组的连续子序列。
示例 1:
输入: nums = [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
来源:力扣(LeetCode)
链接:leetcode.cn/problems/ma…
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码
法一:动态规划
/**
* 动态规划;
* 难点在于对于0和负数的处理,
* 1。 第一反应可以拆成子数组去做;会增加代码难度
* 2。 可以改变下参考下l53的题目;表达式改为 dp[i] = Max(dp[i-1]*n[i],n[i]);这样当遇到0之后,就会自动隔断前面的数,从0开始计算
* 然后在dp[i]数组中取最大值即可
* @param nums
* @return
*/
public int maxProduct(int[] nums) {
// x:代表从下标。开始;y:代表长度为y
// 看上面的解释,也就不需要二维了
// int[][] dp = new int[nums.length][nums.length];
// for (int i = 0; i < nums.length; i++) {
// dp[i][1] = nums[i];
// }
// 用来存储当下标为i时的最大值
int maxV[] = new int[nums.length];
// 因为乘积可能存在负负德正的情况,存储下最小值
int minV[] = new int[nums.length];
maxV[0] = nums[0];
minV[0] = nums[0];
for (int i = 1; i < nums.length; i++) {
// 两个重要的性质:1. Math.max(nums[i],nums[i]*maxV[i-1])这个是为了去掉0;也就是看看是否要从i位置重新开始计算
// 2. 同时存储最大最小值;是因为可能有负数,负负得正,会得到更大值
// 最大值的计算:主要就是最大值*当前元素,最小值(负数)*当前元素;取最大值
maxV[i] = Math.max(Math.max(nums[i],nums[i]*maxV[i-1]),nums[i]*minV[i-1]);
// 最小值同样道理;当前可能为负数,负负得正,就娶不到这个值了;需要加上max[]的乘机比较
minV[i] = Math.min(Math.min(nums[i],nums[i]*minV[i-1]),nums[i]*maxV[i-1]);
}
int max = Integer.MIN_VALUE;
for (int i = 0; i < nums.length; i++) {
max = Math.max(max,maxV[i]);
}
return max;
}
遍历存储法:
public int maxProduct(int[] nums) {
int min = 1;
int max = 1;
int res = Integer.MIN_VALUE;
for (int i = 0; i < nums.length; i++) {
// 如果当前数为负数;则交换正负数
if(nums[i]<0){
int temp = min;
min = max;
max = temp;
}
// 然后运用规则,处理0;并存下到当前位置的最大最小值
max = Math.max(max*nums[i],nums[i]);
min = Math.min(min*nums[i],nums[i]);
// 计算最大值
res = Math.max(res,max);
}
return res;
}
解析
法一:动态规划
题目的主要两个点在于;负数和0的处理
对于0的处理: 用以前遇到过的子数组最大和处理即可,即dp[i] = Math(num[i]*dp[i-1],num[i]);这样当遇到0时,自然会抛弃前面的字符串,从i再开始计算
对于负数的处理: 因为负数的性质在于,前面的乘积如果是负数,虽然不是最大值,但是后面如果再遇到负数,就负负得正,可能是最大值;所以还需要额外存储一下前面的最小值,即:
maxV[i] = Math.max(Math.max(nums[i],nums[i]*maxV[i-1]),nums[i]*minV[i-1]);
maxV[i] = Math.min(Math.min(nums[i],nums[i]*minV[i-1]),nums[i]*maxV[i-1]);
具体看代码
法二:两个变量存储
法二是在leetcode上看到的一个解法,理论一下,只是对于这个处理更加简洁,主要就是把最大最小值存储下来;遇到负数就交换,保证存储的最大最小值是对的;处理0的方法都是一致的 引用:
遍历数组时计算当前最大值,不断更新
令imax为当前最大值,则当前最大值为 imax = max(imax * nums[i], nums[i])
由于存在负数,那么会导致最大的变最小的,最小的变最大的。因此还需要维护当前最小值imin,imin = min(imin * nums[i], nums[i])
当负数出现时则imax与imin进行交换再进行下一步计算