152. 乘积最大子数组
难度:中等 2020/5/18每日一题打卡√
今天也是磨磨蹭蹭只想完成任务的一天
题目描述
解题思路
参考题解:动态规划(理解无后效性)
1、记录最大最小累计和
这道题和最大子序和那道好像,也是用动态规划,但是比那个要复杂一点。
首先因为数字都是整数,所以如果乘一个正数肯定会使结果增大,乘一个负数会让结果减小。问题在于负数的个数不确定,如果时偶数个负数的话,那整个数组所有的元素累乘就是最后结果,如果负数个数是奇数个就不能这样。
所以在遍历计算最大值的时候,也要顺便把最小值记录下来,因为如果乘一个负数,最大值就会变成最小值,最小值就会变成最大值
/*
* 152. 乘积最大子数组
* 2020/5/18 每日一题
*/
public static int maxProduct(int[] nums) {
int max = 1,min = 1,re = Integer.MIN_VALUE;
for (int i = 0; i < nums.length; i++) { //依次找到乘积最大值
if(nums[i] < 0) { //如果要乘的数小于0
int temp = max;
max = min;
min = temp;
}
max = Math.max(max*nums[i], nums[i]);
min = Math.min(min*nums[i], nums[i]);
re = Math.max(re, max);
//System.out.println(max+" "+min+" "+re);
}
return re;
}
这个方法是看评论的,当时觉得有点难理解,看看运行过程之后好懂一点。
样例:2,3,-2,4,-1,8,0,23,-1,56,0,0,0
过程:
2 2 2
6 3 6
-2 -12 6 //反转
4 -48 6
48 -4 48 //反转
384 -32 384
0 0 384
23 0 384
0 -23 384
56 -1288 384
0 0 384
0 0 384
0 0 384
记录最小值的作用在于遇到负数的时候能正确的得到负数或者负负得正
如果没有这个的话,是这样,遇到负数就会丢掉一部分结果
2 2
6 6
-2 6
4 6
-1 6
8 8
0 8
23 23
-1 23
56 56
0 56
0 56
0 56
2、从左往右和从右往左遍历两次
也是一种有点神奇的方法,不知道他们怎么想到的
还是分情况:(先假设数组里不存在0)
如(1,-2,3,-4,5)
1、负数个数是偶数,比较简单,如果是这样那从左往右遍历的时候就能找到最大值
2、负数个数是奇数,从左往右就得不到正确答案,那从右往左再开始一次
1,-2,-3,-4,5
奇数个数的负数,要么不要最左边那个负数,要么不要最右边那个负数,比较两种那种最大
public int maxProduct(int[] nums) {
int max = Integer.MIN_VALUE,sum = 1;
for (int i = 0; i < nums.length; i++) { //从左边开始
sum = sum*nums[i];
max = Math.max(max, sum);
if(nums[i] == 0) //如果遇到1,重新开始
sum = 1;
}
sum = 1;
for (int i = nums.length-1; i > 0; i--) {
sum = sum*nums[i];
max = Math.max(max, sum);
if(nums[i] == 0) //如果遇到1,重新开始
sum = 1;
}
return max;
}