152. 乘积最大子数组

文章介绍了如何使用动态规划解决寻找整数数组中乘积最大的非空连续子数组的问题。通过维护最大值(maxF)和最小值(minF)两个状态,遵循递推公式更新,最后得到最大乘积。文章提供了两种解决方案,一种使用额外数组存储状态,另一种优化后仅使用常量空间。时间复杂度均为O(n)。
摘要由CSDN通过智能技术生成

152.乘积最大子数组

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

测试用例的答案是一个 32-位 整数。

子数组 是数组的连续子序列。

示例 1:

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

示例 2:

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

解题思路:
凡是涉及到子序列问题,应该第一时间想到使用动态规划。
我们还是正常使用动归五部曲:

1、确定dp数组(dp table)以及下标的含义
我们用 dp[i] 来表示第以第 i 个结尾的最大连续子序列的乘积。

2、确定递推公式
dp[i] = dp[i -1] * num[i];为什么我们能推导出这个公式?你想一下这里是连续子序列的乘积啊,注意我们 dp[i] 的定义,以第 i 个结尾的最大连续子序列的乘积,那么我们当前 i 的最大乘积 dp[i] = 前一个 * 当前值,举例,1,2,3,4.比如我们当前值是 3 ,那么它的前一个是 12 当前值 是 3 ,12* 3 =6,ok,懂了?但是你别忽略了,我们这里有负数的呀,我们需要的是乘积,负负是得正的,而且负数越小乘积越大,所有我们需要把正负分开来处理。
那么就是

maxF[i] = Math.max(maxF[i-1] * nums[i],Math.max(nums[i],minF[i-1] * nums[i]));
minF[i] = Math.min(minF[i-1] * nums[i],Math.min(nums[i],maxF[i-1] * nums[i]));

有人好奇为什么公式后面会有max(nums[i],minF[i-1] * nums[i]) /Math.min(nums[i],maxF[i-1] * nums[i])
负负得正,是不是有可能比我们 正正相乘要大,所以也需要从中间求出最大值,所以才有了Math.max(nums[i],minF[i-1] * nums[i]),下面因为我们需要维护最小值,同理出现了min(nums[i],maxF[i-1] * nums[i])
ok到这了,那么我们是不是需要维护两个 dp 数组了。

3、dp 数组初始化
从递归公式我们可以看出 dp[i] 依赖与 dp[i-1] 所以 dp[0] 应该是多少呢?

根据 dp[i] 的定义,很明显 dp[0] 应为 nums[0] 即dp[0] = nums[0]以此类推,其实就是默认三个数组都一样,maxF、minF、num

4、确定遍历顺序
递推公式中 dp[i] 依赖于 dp[i - 1 ]的状态,需要从前向后遍历。

5、举例推导dp数组
在推导公式中已经说明了,你也可以写出代码,debug 推导。看哪里有误。

代码:

public static int maxProduct(int[] nums) {
		//dp[i] 来表示第以第 i 个结尾的最大连续子序列的乘积。
		int len = nums.length;
		int[] maxF = new int[len];
		int[] minF = new int[len];
		//初始化dp数组
		System.arraycopy(nums, 0, maxF, 0, len);
		System.arraycopy(nums, 0, minF, 0, len);
		//遍历顺序
		for (int i = 1; i < len; i++) {
			//递推公式
			maxF[i] = Math.max(maxF[i-1] * nums[i],Math.max(nums[i],minF[i-1] * nums[i]));
			minF[i] = Math.min(minF[i-1] * nums[i],Math.min(nums[i],maxF[i-1] * nums[i]));
		}
		//推导结果
		int res = maxF[0];
		for (int i = 1; i < len; i++) {
			res = Math.max(res,maxF[i]);
		}
		return res;
	}

时间复杂度:o(n)
空间复杂度:o(n)

官方优化后:

 public int maxProduct(int[] nums) {
        int maxF = nums[0], minF = nums[0], ans = nums[0];
        int length = nums.length;
        for (int i = 1; i < length; ++i) {
            int mx = maxF, mn = minF;
            maxF = Math.max(mx * nums[i], Math.max(nums[i], mn * nums[i]));
            minF = Math.min(mn * nums[i], Math.min(nums[i], mx * nums[i]));
            ans = Math.max(maxF, ans);
        }
        return ans;
    }

时间复杂度:o(n)
空间复杂度:o(1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值