LeetCode152. 乘积最大子数组

签到题,中等题。

给你一个整数数组 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;
    }

猜你喜欢

转载自blog.csdn.net/lfanchenyu/article/details/106198427