152. 乘积最大子数组

题目描述:

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

示例 1:

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

示例 2:

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

思路一:暴力双循环

从当前元素开始,不断累乘后面的每一个元素,维护一个最大值
 1 class Solution {
 2     public int maxProduct(int[] nums) {
 3 
 4         int max = -1 << 30;
 5         int len = nums.length;
 6         for(int i = 0; i < len; i++){
 7             int product = nums[i];
 8             max = Math.max(product, max);
 9             for(int j = i + 1; j < len; j++){
10                 product *= nums[j];
11                 max = Math.max(product, max);
12             }
13         }
14         return max;
15     }
16 }
leetcode 执行用时:136 ms > 5.34%, 内存消耗:38.3 MB > 90.51%

复杂度分析:

时间复杂度:O(n2)。双重循环,所以时间复杂度为 O(n2)。
空间复杂度:O(1)。

思路二:动态规划

因为当当前元素为负时,连续子数组的临时最大值乘以该元素后会变成连续子数组的临时最小值,连续子数组的临时最小值乘以该元素后会变成连续子数组的临时最大值,所以要同时记录当前最大值和最小值。
当当前元素为负时,临时最小值和临时最大值进行交换,始终保持临时值乘以当前元素后的最大值保存在 tmpMax中,最小值保存在 tmpMin中。
 1 class Solution {
 2     public int maxProduct(int[] nums) {
 3 
 4         int max = -1 << 30;
 5         int tmpMax = 1, tmpMin = 1;
 6         int len = nums.length;
 7         for(int i = 0; i < len; i++){
 8             // 当当前元素为负时,临时最大值和临时最小值互换
 9             if(nums[i] < 0){
10                 int temp = tmpMax;
11                 tmpMax = tmpMin;
12                 tmpMin = temp;
13             }
14             tmpMax = Math.max(tmpMax * nums[i], nums[i]);
15             tmpMin = Math.min(tmpMin * nums[i], nums[i]);
16             max = Math.max(tmpMax, max);
17         }
18         return max;
19     }
20 }
leetcode 执行用时:2 ms > 89.18%, 内存消耗:38.5 MB > 85.41%, 可以看到,时间花费小了很多,因为复杂度降低了一个级别。

复杂度分析:

时间复杂度:O(n)。只有一层循环,所以时间复杂度为 O(n)。
空间复杂度:O(1)。