LeetCode 152.乘积最大子数组

本文探讨了如何找出整数数组中乘积最大的连续子数组,并提供了动态规划和分治法两种解决方案。通过实例解析,深入理解算法原理。

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

题目

给你一个整数数组 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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解区

这是一道标签为动态规划的题目
从这个角度考虑的话,我们可以定义数组

dp[i][2];
其中dp[i][0]表示从 0 到位置 i 的最大乘积和
dp[i][1] 表示从 0 到位置 i 的最小乘积和
需要保留最小值的原因是可能存在负值乘负值的情况

代码如下

public class Solution {
    public int maxProduct(int[] nums) {
        int n = nums.length;
        int[][] dp = new int[n][2];
        dp[0][0] = nums[0];
        dp[0][1] = nums[0];
        int ans = nums[0];
        for (int i = 1; i < n; i++) {
            dp[i][0] = Math.max(nums[i],Math.max(nums[i] * dp[i - 1][0] , nums[i]*dp[i-1][1]));
            dp[i][1] = Math.min(nums[i],Math.min(nums[i] * dp[i - 1][0] , nums[i]*dp[i-1][1]));
            ans = Math.max(ans , dp[i][0]);
        }
        return ans;
    }
}

那么问题来了,不难发现对于每一个dp[i] 我们其实每次只需要 dp[i-1] 即可,
所以此处并不需要定义数组,可以将其简化到使用几个变量

public class Solution {
    public int maxProduct(int[] nums) {
        int n = nums.length;
        int premax = nums[0];
        int premin = nums[0];
        int curmax;
        int curmin;
        int ans = nums[0];
        for (int i = 1; i < n; i++) {
            curmax = Math.max(nums[i] , Math.max(premax*nums[i] , premin*nums[i]));
            curmin = Math.min(nums[i] , Math.min(premax*nums[i] , premin*nums[i]));
            ans = Math.max(ans , curmax);
            premax = curmax;
            premin = curmin;
        }
        return ans;
    }
}

这是两种较为优质的解法,但是我们还可以仔细地探究这道题目,求最大子数组乘积 和求最大子数组和存在一定的相似性,所以我们也可以尝试使用分治的方法来解决这道题

class Solution {
    public int maxProduct(int[] nums) {
    	return maxSub(nums,0,nums.length-1);
    }
    public  int maxSub(int[]nums,int left,int right ){
        // maxrightbordermultipy , minrightbordermultipy,从中间到右边边的最大和最小乘积
		int maxrightbordermultipy = Integer.MIN_VALUE,minrightbodermultipy = Integer.MAX_VALUE;
        // maxleftbordermultipy , minleftbordermultipy,从中间到左边的最大和最小乘积
        int minleftbordermultipy = Integer.MAX_VALUE, maxleftbordermultipy = Integer.MIN_VALUE;
        // leftmaxmultipy -> 左边子数组的最大乘积; rightmaxmultipy -> 右边子数组的最大乘积;
		int leftmaxmultipy = Integer.MIN_VALUE,rightmaxmultipy = Integer.MIN_VALUE;				
		//使用leftbordermultipy , rightbodermultipy 来记录向左,右乘积时的值
        int leftbordermultipy = 1,rightbordermultipy = 1;
        //记录跨越中间部分子数组的最大值
		int midmaxmultipy = 0;
		int mid = left+(right-left)/2;
		if(left==right){
			return nums[left];
		}else if(left+1 == right){
            return Math.max(nums[left] ,Math.max(nums[right], nums[left]*nums[right]));
        }

		leftmaxmultipy = maxSub(nums,left,mid);		
		rightmaxmultipy = maxSub(nums,mid,right);	
		
		for(int i = mid;i >= left ;i--){
			leftbordermultipy *= nums[i];
			if(leftbordermultipy > maxleftbordermultipy)
				maxleftbordermultipy = leftbordermultipy;
            if(leftbordermultipy < minleftbordermultipy)
                minleftbordermultipy = leftbordermultipy;
            if(leftbordermultipy == 0)
                break;
		}
		
		for(int i = mid+1 ;i <= right;i++){
			rightbordermultipy *= nums[i];
			if(rightbordermultipy > maxrightbordermultipy)
				maxrightbordermultipy = rightbordermultipy;
            if(rightbordermultipy < minrightbodermultipy)
                minrightbodermultipy = rightbordermultipy;
            if(rightbordermultipy == 0)
                break;
		}
        //取得中间部分子数组的最大乘积
        midmaxmultipy = Math.max(maxleftbordermultipy,//左大
            Math.max(maxrightbordermultipy, //右大
                Math.max(leftbordermultipy*rightbordermultipy,//左边界*右边界
                    Math.max(minleftbordermultipy*minrightbodermultipy,//左小*右大
                        Math.max(minleftbordermultipy*maxrightbordermultipy,//左小*右小
                            Math.max( maxleftbordermultipy*minrightbodermultipy, // 左大*右小
                                    maxleftbordermultipy*maxrightbordermultipy //左大*右大
                            )
                        )
                    )
                )
            )
        );
		return Math.max(leftmaxmultipy,Math.max(rightmaxmultipy,midmaxmultipy));
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值