最大连续乘积子序列几种解法比较有感

给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。

示例 1:

输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:

输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-product-subarray

自己的思路

刚开始是想根据零切分数组为几个段,然后得出几个段的最大值,最后比较他们。

但是遇到分析正负数的大小值和奇偶个数的时候谈论就不知道如何找出他们的最大值了。
主要是卡在状态转移方程究竟是怎样写。其实当没有思路写出状态转移方程的时候可以不必要强求,状态转移是希望前一个或者后一个给当前的值提供一个转换,但是这道题在于会出现很多不同的情况需要回来分支。比如当前扫描到一个负数的时候应不应该就将它加入到结果数组中呢?

如果再依次进行分类讨论的话就有点复杂了,需要用纸质记录下来。

题解记录

发现题解的状态转移是定义以 下标i为结束点的最大乘积和以下标i为结束点的最小乘积,他们都记录下来。这样如果遇到负数的时候,我们希望乘上前一个的值尽可能的小,遇到正数的时候尽可能地大。一次遍历的过程中就记录下来了最大值的序列。

最后遍历最大值序列,得出最大值。

讨论的记录,统一化处理和滑动窗口。

1、先以0作为切分的数组,先正序扫描,每一次扫描乘上的结果都有可能是最大值。倒序扫描在处理奇数的时候,最大值一定会出现在正序或者负序当中,这个想法和总结真的是太妙了。正序和倒序的比较是一样的。

class Solution {
    public int maxProduct(int[] nums) {
        //左右扫一遍,记录最大值。
        int muti=1,max_value=nums[0];
        for(int num:nums){
            muti*=num;
            if(max_value<muti){
                max_value=muti;
            }
            if(num==0)muti=1;
        }
        muti=1;
        for(int i=nums.length-1;i>=0;--i){
            muti*=nums[i];
            if(max_value<muti){
                max_value=muti;
            }
            if(nums[i]==0)muti=1;            
        }

        return max_value;
    }
}

滑动窗口:
在向右扩张的时候遇到0或者遇到结尾就要停下来左缩小。左缩小到下标相等的时候。还是要考虑和零比较啊,发现滑动窗口对于连续序列的解答是有用的,它通常包含一个前进和回退操作。
在回退的时候要尤其小心,得看看是否已经包含边界条件的比较了。我写的回退操作就忘记比较零的大小了,还有回退的时候将自身也除下去了,不仅感慨,坑也不少啊。

自己写的:

class Solution {
    public int maxProduct(int[] nums) {
        int n = nums.length;
        if(n==1)return nums[0];
        int i=0,j=0,muti=1,max_value=Integer.MIN_VALUE;

        while(i<n&&j<=n){
            if(j==n||nums[j]==0){
                //缩小窗口

               while(i<j&&i!=j-1){
                    muti/=nums[i];
                    max_value=Math.max(max_value,muti);
                    i++;
               }
               if(j<n&&nums[j]==0)max_value=Math.max(max_value,0);
               j++;
               muti=1;
               i=j;
            }
            else{
                //扩大窗口:
                muti*=nums[j];
                max_value=Math.max(max_value,muti);
                j++;
            }
        }
        return max_value;
    }
}

别人写的:

class Solution {
public int maxProduct(int[] nums) {
        //思想:双指针,遇到0左指针开始收敛
        int n = nums.length;
        if(n == 1) return nums[0];
        int max = Integer.MIN_VALUE;
        int muti = 1;
        int l = 0, r = 0;
        while(r <= n && l < n){
            if(r == n){//如果右指针到达最后一个位置,左指针开始滑动
                muti /= nums[l++];
            }else{
                if(nums[r] != 0){//如果右指针所指元素不为0,右滑,更新muti
                    muti *= nums[r++];
                }else if(l < r){//右指针为0,开始左滑,更新muti
                    muti /= nums[l++];
                }else{//左右指针都滑到了0的位置,更新max,左右指针指向下一位置重新滑动
                    max = Math.max(max, 0);
                    ++l;
                    ++r;
                }
            }
            //只要左右指针没有指向同一位置,更新max(指向同一位置说明左滑到顶了,记录的muti一定为1,但并不是乘积值)
            if(l != r) max = Math.max(max, muti);
        }
        return max;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值