152.乘积最大子数组

Posted by 令德湖周杰伦 on 07-21,2020

题目

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

示例

示例1
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例2
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

分析

暴力解法:

2层循环,第一层:记录从谁开始算,第二层:从开始往后累乘并记录该层最大值。最后再比较出每层最大值的最大值。
时间复杂度: O(n * n)
代码如下:

private int maxProductByBL(int[] nums) {
        int length = nums.length;
        if(length ==1){
            return nums[0];
        }
        int max = nums[0];
        for (int i = 0; i < length; i++) {
            int levelMax = nums[i];
            int temp = nums[i];
            for (int j = i+1; j < length; j++) {
                temp *= nums[j];
                if(temp>levelMax){
                    levelMax = temp;
                }
            }
            if(levelMax>max){
                max = levelMax;
            }
        }
        return max;
    }

动态规划DP:

定义状态: dp[i][j] 为第i个元素时(乘积必须包含nums[i]元素)的最大子乘积值(j=1)和最小子乘积值(j=0)。包含的意思为,有 {-1 -2 -3} 当i=2时,子乘积包含 {-1 -2 -3} 或者 {-2 -3} 或者 {-3}

定义2个数组的原因:可能会出现负负得正的情况,即-10 * -2 =20,需要去记录一个最大值和一个最小值
状态方程:
nums[i]<=0:小于等于0时
//最小值为:当前元素本值或者前一个最大值乘本值 中最小的
dp[i][0] = Math.min(nums[i],dp[i-1][1] * nums[i]);
//最大值为:当前元素本值或者前一个最小值乘本值 中最大的
dp[i][1] = Math.max(nums[i],dp[i-1][0] * nums[i]);
nums[i]>0:大于0时
//最小值为:当前元素本值或者前一个最小值乘本值 中最小的
dp[i][0] = Math.min(nums[i],dp[i-1][0] * nums[i]);
//最大值为:当前元素本值或者前一个最大值乘本值 中最大的
dp[i][1] = Math.max(nums[i],dp[i-1][1] * nums[i]);

初始状态:
dp[0][0] = num[0]
dp[0][1] = num[0]

返回:
max{dp[i][1]}

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

代码如下

int length = nums.length;
        if(length ==1){
            return nums[0];
        }
        int [][]dp = new int[length][2];
        dp[0][0] = nums[0];
        dp[0][1] = nums[0];
        int max = dp[0][1];
        for (int i = 1; i <length ; i++) {
            if(nums[i]<=0){
                dp[i][0] = Math.min(nums[i],dp[i-1][1] * nums[i]);
                dp[i][1] = Math.max(nums[i],dp[i-1][0] * nums[i]);
            }else{
                dp[i][0] = Math.min(nums[i],dp[i-1][0] * nums[i]);
                dp[i][1] = Math.max(nums[i],dp[i-1][1] * nums[i]);
            }
            if(dp[i][1]>max){
                max = dp[i][1];
            }
        }
        return max;

动态规划upgrade

状态、方程、初始、返回 都一样。但是上一个解法的空间复杂度为O(n),通过观察,其实在遍历的过程中,只需要记录上一个的最大值和最小值即可,然后最大值也在max中保存,所以使用滚动数组的方法,将空间复杂度将为O(1)。

代码如下:

private int maxProductByDP1(int[] nums) {
        int length = nums.length;
        if(length ==1){
            return nums[0];
        }
        int [][]dp = new int[2][2];
        dp[0][0] = nums[0];
        dp[0][1] = nums[0];
        int max = dp[0][1];
        for (int i = 1; i <length ; i++) {
            //滚动数组,如果m=1那么n=0,如果m=0那么n=1
            int m = i%2;
            int n = m == 0 ? 1 : 0;
            if(nums[i]<=0){
                dp[m][0] = Math.min(nums[i],dp[n][1] * nums[i]);
                dp[m][1] = Math.max(nums[i],dp[n][0] * nums[i]);
            }else{
                dp[m][0] = Math.min(nums[i],dp[n][0] * nums[i]);
                dp[m][1] = Math.max(nums[i],dp[n][1] * nums[i]);
            }
            if(dp[m][1]>max){
                max = dp[m][1];
            }
        }
        return max;
    }