签到题,中等题。
给你一个整数数组 nums
,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
示例 1:
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
这种题第一想法就是动态规划的思想,但是涉及到乘积与加减不一样,他很可能负负得正取到更大的。
我就没想到除了当前最大以外,再设一个当前最小值,这样就可以避免遍历到下一个为负的情况了。
我的代码逻辑是:以0为分段标记,记录当前段的值tmpNum,当前段中遇到第一个负值前的积firstFuNum,当前段中最后一个负值到结尾的乘积lastFuNum。当前段结束后如果tmpNum为正,那就与resNum取较大值,如果为负,那就要tmpNum除以firstNum,除以lastNum这两个值与resNum比较。这会有一个问题,就是该段只有一个负数,负数、负数为正数,反而错了,这个加了一个标记singleFu。
遍历到分以下情况:
1.该值不为0(又分为两种,上一个是0和上一个不是0,这个区别在只有一个负数,周围都是0时有区别)
2.该值为0 (又分为结束的这段只有一个负数和普通的)这里要计算结束的上段最大值。
先上自己的代码,写的比较乱,运行时间还算少。
public int maxProduct(int[] nums) {
int resNum = Integer.MIN_VALUE;
int tmpNum = 0;//不为0的区域段乘积
int firesFuNum = 0;//区域段中第一个负数之前的乘积(包括第一个负数)
int lastFuNum = 0;//区域段中最后一个负数之后的乘积(包括最后一个负数)
boolean isZero = true;//当前是否为0
boolean haveFuNum = false;//是否有负数,用于记录firstNum的结束标记
boolean singleFu = false;//结束的段是否只有一个负数
for (int i = nums.length-1; i >= 0; --i){
if (nums[i] != 0){//该值不是0
if (isZero){//区域段的第一个值
tmpNum = nums[i];
firesFuNum = nums[i];
lastFuNum = nums[i];
isZero = false;
if (nums[i] < 0){
haveFuNum = true;
singleFu = true;
}
}else {//不是区域段的第一个值
if (singleFu)
singleFu = false;
tmpNum = tmpNum * nums[i];
if(!haveFuNum){
firesFuNum = firesFuNum * nums[i];
}
if (nums[i] > 0){
lastFuNum = lastFuNum * nums[i];
}else {
haveFuNum = true;
lastFuNum = nums[i];
}
}
resNum = Math.max(resNum,tmpNum);
}
else {//该值为0
if(singleFu){//上段只有一个负数
resNum = Math.max(resNum,0);
singleFu = false;
}else {//上段不是只有一个负数
if(tmpNum < 0)
tmpNum = Math.max(tmpNum/firesFuNum,tmpNum/lastFuNum);
resNum = Math.max(resNum,tmpNum);
}
firesFuNum = 0;
lastFuNum = 0;
tmpNum = 0;
isZero = true;
haveFuNum = false;
}
if(i == 0 && nums[i] != 0){//遍历完最后一个结点
if(singleFu){
resNum = Math.max(nums[0],resNum);
break;
}
if (tmpNum < 0){
tmpNum = Math.max(tmpNum / firesFuNum, tmpNum / lastFuNum);
}
resNum = Math.max(resNum, tmpNum);
}
}
return resNum;
}
接下来是官方考虑到不仅要存储最大的,同时要存储最小的,就写的超级简单了。这里是自己写的java。果然简单明了,就因为没想到记录当前为终点的最小值,就写成了那么麻烦的。。。
扫描二维码关注公众号,回复:
11529915 查看本文章
public int maxProduct(int[] nums) {
int res = nums[0];//结果初始化为第一个点
int tmpMax = nums[0];//该点为终点的最大乘积
int tmpMin = nums[0];//该点为终点的最小乘积
int lengthNums = nums.length;
if(lengthNums == 1)
return nums[0];
for (int i = 1; i < lengthNums; ++i){
int indexMax = Math.max(tmpMax*nums[i],tmpMin*nums[i]);
int indexMin = Math.min(tmpMax*nums[i],tmpMin*nums[i]);
tmpMax = Math.max(indexMax,nums[i]);
tmpMin = Math.min(indexMin,nums[i]);
res = Math.max(res,tmpMax);
}
return res;
}