LeetCode 152.乘积最大子数组

144 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情

开启新一轮的卷啦 ~

题目:给定一个数组nums,要求找出该数组中乘积最大的非空连续子数组,并且返回该子数组对应的乘积。

解题思路

本题的第一印象就是 LeetCode 53最大子数组和 ,和本题的区别只要加变成了乘,动态规划的转移方程都是:

dp[i]=Math.max(dp[i1]nums[i],nums[i])dp[i] = Math.max(dp[i-1]*nums[i], nums[i])

于是按照该题的思路可得到如下代码:

public int maxProduct(int[] nums) {
        int max=nums[0];
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        for(int i=1;i<nums.length;i++){
            dp[i] = Math.max(dp[i-1]*nums[i], nums[i]);
            max = Math.max(max, dp[i]);
        }
        return max;
    }

不出意外报错了,我们可以想象一个数组[-4, -1, -2, -3],当数组遍历到-2之前,系统保存的最大值是4,而dp[i-1]保存的是4,到了-2这个索引的时候,则dp[i]变成了-2max还是4,按照循环完成,最终保存的最大值也就是6,但实际最大值是24,这是因为我们只保存了最大值,而在遍历到负数的时候,最大值就变成了最小值,当再次遇到负数就丢失了使最小值变成最大值的机会。

因此我们在本题中不仅要保存最大值还需要保存最小值,在每次判断时候需要判断最小值可不可能成为最大值,可得代码如下:

public int maxProduct(int[] nums) {
        int[] maxProduct = new int[nums.length];
        int[] minProduct = new int[nums.length];
        maxProduct[0] = nums[0];
        minProduct[0] = nums[0];
        for(int i=1;i<nums.length;i++){
            maxProduct[i] = Math.max(maxProduct[i-1]*nums[i], Math.max(minProduct[i-1]*nums[i], nums[i]));
            minProduct[i] = Math.min(minProduct[i-1]*nums[i], Math.min(maxProduct[i-1]*nums[i], nums[i]));
        }
        int max = Integer.MIN_VALUE;
        for(int j=0;j<maxProduct.length;j++){
            max = Math.max(max, maxProduct[j]);
        }

        return max;
    }

上述代码的时间复杂度和空间复杂度都是O(n)O(n)。对于动态规划一般都可以对空间复杂度进行优化,本题也不例外,一般就是将前一个状态保存赋给下一个状态即可,可得如下代码:

public int maxProduct(int[] nums) {
        int maxProduct = nums[0];
        int minProduct = nums[0];
        for(int i=1;i<nums.length;i++){
            // 我们需要记录上一个状态,此方式无法记录
            maxProduct = Math.max(maxProduct*nums[i], Math.max(minProduct*nums[i], nums[i]));
            minProduct = Math.min(minProduct*nums[i], Math.min(maxProduct*nums[i], nums[i]));
        }

        return maxProduct;
    }

但本题不能使用这种方法,仔细看循环中,两个变量是同时更新的,也就是没有顺序,但上述代码会先更新maxProduct,而在minProduct的计算中用到了maxProduct,因此会报错,此时需要使用两个变量记录一下前一个状态,修改代码如下:

public int maxProduct(int[] nums) {
        int maxProduct = nums[0];
        int minProduct = nums[0];
        int maxAns = nums[0];
        for(int i=1;i<nums.length;i++){
            int maxPre = maxProduct;
            int minPre = minProduct;
            maxProduct = Math.max(maxPre*nums[i], Math.max(minPre*nums[i], nums[i]));
            minProduct = Math.min(minPre*nums[i], Math.min(maxPre*nums[i], nums[i]));
            maxAns = Math.max(maxAns, maxProduct);
        }

        return maxAns;
    }

上述代码时间复杂度为O(n)O(n),空间复杂度为O(1)O(1)