. - 力扣(LeetCode). - 备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/maximum-product-subarray/description/
题目分析
题目要求找出给定整数数组中乘积最大的连续子数组,并返回这个乘积。
思路分析
这道题可以使用动态规划来解决。关键是维护两个状态数组 f
和 g
,其中:
f[i]
表示以第i
个元素结尾的最大子数组乘积;g[i]
表示以第i
个元素结尾的最小子数组乘积。
为什么需要维护最小值 g[i]
呢?因为负数乘以负数会变成正数,所以需要同时维护最大值和最小值来应对乘积的变化情况。
状态转移方程
对于第 i
个元素:
f[i]
的计算方式:f[i] = max(nums[i], f[i-1] * nums[i], g[i-1] * nums[i])
- 即当前元素自身、前一个最大乘积乘以当前元素、前一个最小乘积乘以当前元素的最大值。
g[i]
的计算方式:g[i] = min(nums[i], f[i-1] * nums[i], g[i-1] * nums[i])
- 即当前元素自身、前一个最大乘积乘以当前元素、前一个最小乘积乘以当前元素的最小值。
初始化
初始状态为:
f[0] = nums[0]
g[0] = nums[0]
最终结果
最终结果即 f
数组中的最大值,即为整个数组中乘积最大的连续子数组的乘积。
代码实现
package study1.day12;
/*
*152. 乘积最大子数组
* 思路分析:
* 1.状态表示 f[i]以i位置为结尾的最大子数组的和
* g[i]以j位置为结尾的最小子数组的和
* 在最大值中 > 0 乘最大值 < 0 乘最小值
* 2.状态转移方程 f[i] = max(sum[i],sum[i] * f[i - 1],sum[i] * g[i - 1])
* 在最小值中 > 0 乘最最小值 < 0 乘最大值
* g[i] = min(sum[i],sum[i] * g[i - 1],sum[i] * f[i - 1])
* 3.初始化 f[0] = 1 g[0] = 1 (任何数 * 1都等于任何数)
*
* 总结一下: 本题主要是两个dp一个记录最大 一个记录最小
* 因为本题存在负数当为负数的时候最大值就会变成最小值然后再转换
* 所以本题一定要分正数在划分一下dp的最小状态
* */
public class test5 {
public int maxProduct(int[] nums) {
int n = nums.length;
//1.创建f g 数组用来存储历史记录
int[] f = new int[n];
int[] g = new int[n];
//2.初始化
f[0] = g[0] = 1;
int retMax = Integer.MIN_VALUE;
//3.填表
for (int i = 1; i <= n; i++) {
f[i] = Math.max(Math.max(nums[i - 1],f[i - 1] * nums[i - 1]),g[i - 1] * nums[i - 1]);
retMax = Math.max(retMax,f[i]);
g[i] = Math.min(Math.min(nums[i - 1],g[i - 1] * nums[i - 1]),f[i - 1] * nums[i - 1]);
}
return retMax;
}
}