乘积最大子数组

博客介绍了LeetCode 152题目的解法,包括动态规划、三层for循环、优化后的动态规划和交换最大最小四种方法。动态规划解法中,解释了dp数组的含义和转移方程,优化后的动态规划解法减少了空间复杂度。交换最大最小的解法通过维护最大值和最小值,有效地解决了包含负数的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LeetCode 152

乘积最大子数组

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

示例 1:

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

示例 2:

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

解法1:动态规划

解题思路:

用动态规划的思想,dp数组的含义就是从第i个数字到第j个数字的乘积和

有以下转移方程

dp[i][j] = dp[i][j-1]*nums[j];

代码如下:

class Solution {
    public int maxProduct(int[] nums) {
        if(nums.length==1)
            return nums[0];
        int[][] dp = new int[nums.length][nums.length];
        int res = Integer.MIN_VALUE;
        for(int i=0; i<dp.length; i++) //初始化
        {
            dp[i][i] = nums[i];
            res=Math.max(dp[i][i],res);
        }     
        for(int i=0; i<dp.length; i++)
        {
            for(int j=i+1; j<dp[0].length; j++)
            {
                dp[i][j]=dp[i][j-1]*nums[j];
                res=Math.max(res,dp[i][j]);
            }            
        }
        return res;
    }
}

时间复杂度为O(N^2)

空间复杂度为O(N^2)

该解法超内存限制了

解法2:三层for循环

解题思路:

直接用三层for循环计算每一个子数组的乘积和

class Solution {
    public int maxProduct(int[] nums) {
        if(nums.length==1)
            return nums[0];
        int res = Integer.MIN_VALUE;
        int num;
        for(int i=0; i<nums.length; i++) //以i为结尾的子数组
        {
            for(int j=0; j<=i; j++) //从第0个开始
            {
                num=1;
                for(int k=j; k<=i; k++) //计算从第j到第i的子数组乘积和
                    num*=nums[k];
                res = Math.max(num,res);
            }
        }
        return res;
    }
}

时间复杂度为O(N^3)

空间复杂度为O(1)

超时了

解法3:优化后的动态规划

解题思路:

我们还是用动态规划的思想,但是dp数组的含义是,以i为结尾的子数组乘积和

初始化为

for(int i=start; i<=end; i++)
{
    num*=nums[i];
    dp[i-start]=num;
}

因此我们有这样一个动态转移方程,dp[i]/dp[j]就是表示了从j到i的子数组,如

nums = [2,3,-2,4]

dp = [2,6,-12,-48]

因此-48/2就表示了3,-2,4这一子数组

-48/6就表示了-2,4这一子数组

-48/-12就表示了4这一子数组

因此我们就可以另dp[3] = 上面这一系列子数组的最大值

更新后的值为

dp = [2,6,-12,4]

注意,我们要从后往前更新,因为后面的值不会影响到前面的值

int tmp = dp[i]
tmp = Math.max(tmp, dp[i]/dp[j]) // 0=<j<i
dp[i] = tmp;

还有注意出现0的情况,如果出现0,那么后面的乘积和就都为0了,所以我们把出现0的两边分成两个数组

代码如下:

class Solution {
    public int maxProduct(int[] nums) {
        if(nums.length==1)
            return nums[0];
        int res = Integer.MIN_VALUE;
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(0); //起始地址
        for(int i=0; i<nums.length; i++)
        {
            if(nums[i]==0)
            {
                if(i==0) //如果第一个就是0,那么将其除掉
                {
                    list.remove(0);
                    list.add(1);
                }    
                else //加上0的两边的下标
                {
                    list.add(i-1);
                    list.add(i+1);
                }       
            }
            res = Math.max(res,nums[i]);
        }
        list.add(nums.length-1); //最后面的地址
        for(int i=0; i<list.size(); i+=2) //因为起始和终止地址是成对出现的
        {
            int start = list.get(i);
            int end = list.get(i+1);
            res = Math.max(res,maxProduct(nums,start,end));
        }
        return res;
    }
    public int maxProduct(int[] nums, int start, int end) //动态规划计算最大乘积和
    {
        int[] dp = new int[end-start+1];
        int res = Integer.MIN_VALUE;
        int num=1;
        for(int i=start; i<=end; i++) //初始化
        {
            num*=nums[i];
            dp[i-start]=num;
        }
        for(int i=dp.length-1; i>=0; i--) //从后面往前更新
        {
            int tmp=dp[i];
            for(int j=0; j<i; j++)
            {
                tmp = Math.max(tmp, dp[i]/dp[j]);
            }
            dp[i] = tmp;
            res = Math.max(res,dp[i]);
        }
        return res;
    }
}

时间复杂度为O(N^2)

空间复杂度为O(N)

解法4:交换最大最小

解题思路:

  • 遍历数组时计算当前最大值,不断更新
  • 令imax为当前最大值,则当前最大值为 imax = max(imax * nums[i], nums[i])
  • 由于存在负数,那么会导致最大的变最小的,最小的变最大的。因此还需要维护当前最小值imin,imin = min(imin * nums[i], nums[i])
  • 当负数出现时则imax与imin进行交换再进行下一步计算

代码如下:

class Solution {
    public int maxProduct(int[] nums) {
        int max = Integer.MIN_VALUE, imax = 1, imin = 1;
        for(int i=0; i<nums.length; i++){
            if(nums[i] < 0){ 
                int tmp = imax;
                imax = imin;
                imin = tmp;
            }
            imax = Math.max(imax*nums[i], nums[i]);
            imin = Math.min(imin*nums[i], nums[i]);
            
            max = Math.max(max, imax);
        }
        return max;
    }
}

时间复杂度为O(N)

空间复杂度为O(1)

解法来源

作者:guanpengchn
链接:https://leetcode-cn.com/problems/maximum-product-subarray/solution/hua-jie-suan-fa-152-cheng-ji-zui-da-zi-xu-lie-by-g/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值