一、题目描述
1.1 题目
-
乘积最大子序列
-
给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。
-
示例 1:
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
- 示例 2:
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
1.2 知识点
- 动态规划
1.3 题目链接
二、解题思路
2.1 解题思路一
解法一的核心思想就是:乘积最大的子序列一定是通过连续子序列乘上接下来的一个数而得到的,而接下来的这个数可能为正数,也可能为负数。当接下来的数为正数时,如果前面的子序列也为正数,那么相乘后就可能得到乘积最大的子序列,而如果前面的子序列是一个负数,那么乘上这个正数又有可能变为乘积最小的子序列,但该乘积最小的子序列却可能在接下来乘上一个负数后变成乘积最大的子序列。
所以综上所述,我们应当维护一个乘积最大子序列和一个乘积最小子序列,且当当前元素的值大于最大子序列乘上该值时更新最大子序列为当前元素值,对于最小子序列乘积值亦然,且需要注意的是当当前元素为负数时,应当交换当前的最大乘积子序列和最小乘积子序列。
2.2 解题思路二
该解法是评论中大神提供的解法,具体思路直接引用评论中 nlppupil 的解析:
-
乘积最大子序列有两种情况:
(1)连续几个不包含0的数,且其中负数有偶数个。
(2)0 -
1.如果输入列表不包含0:
有两种情况:
(1)负数有偶数个,那么返回全部乘积
(2)负数有奇数个,那么乘积最大子序列必然包含除最左边或最右边的负数之外的所有负数。在不知道去除那个边界负数的情况下,可以从左到右和从右到左分别计算累积,累积更大的那个就是答案。 -
2.如果输入列表包含0:
以0为分界线,将列表分为若干个不包含0的列表,对每个列表回到1. -
总结到一起,就是建立一个正向累积列表forward[i],使得
if forward[i-1] !=0:
forward[i] = forward[i]*forward[i-1]
else:
forward[i] = 0
- 同理,建立一个反向累计列表backwards,使得
if forward[i+1] !=0:
forward[i] = forward[i]*forward[I+1]
else:
forward[i] = 0
三、实现代码
3.1 代码实现(一)
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;
}
}
3.2 代码实现(二)
class Solution:
def maxProduct(self, A):
B = A[::-1]
for i in range(1, len(A)):
A[i] *= A[i - 1] or 1
B[i] *= B[i - 1] or 1
return max(max(A),max(B))