Leetcode152. Maximum Product Subarray

Leetcode152. Maximum Product Subarray

Given an integer array nums, find the contiguous subarray within an array (containing at least one number) which has the largest product.

Example 1:

Input: [2,3,-2,4]
Output: 6
Explanation: [2,3] has the largest product 6.

Example 2:

Input: [-2,0,-1]
Output: 0
Explanation: The result cannot be 2, because [-2,-1] is not a subarray.

解法一 动态规划

dpMax[i] 表示以第 i 个元素的结尾的子数组的乘积最大的值。

  • nums[i] >= 0 并且dpMax[i-1] > 0dpMax[i] = dpMax[i-1] * nums[i]
  • nums[i] >= 0 并且dpMax[i-1] < 0,此时如果和前边的数累乘的话,会变成负数,所以dpMax[i] = nums[i]
  • nums[i] < 0,此时如果前边累乘结果是一个很大的负数,和当前负数累乘的话就会变成一个更大的数。所以我们还需要一个数组 dpMin 来记录以第 i 个元素的结尾的子数组的乘积最小的值。
    • dpMin[i-1] < 0dpMax[i] = dpMin[i-1] * nums[i]
    • dpMin[i-1] >= 0dpMax[i] = nums[i]

所以状态转移方程为:dpMax[i] = max(dpMax[i-1] * nums[i], dpMin[i-1] * nums[i], nums[i]);
同理,dpMin[i] = min(dpMax[i-1] * nums[i], dpMin[i-1] * nums[i], nums[i]);

public int maxProduct(int[] nums) {
    int n = nums.length;
    if (n == 0) {
        return 0;
    }

    int[] dpMax = new int[n];
    dpMax[0] = nums[0];
    int[] dpMin = new int[n];
    dpMin[0] = nums[0];
    int max = nums[0];
    for (int i = 1; i < n; i++) {
        dpMax[i] = Math.max(dpMin[i - 1] * nums[i], Math.max(dpMax[i - 1] * nums[i], nums[i]));
        dpMin[i] = Math.min(dpMin[i - 1] * nums[i], Math.min(dpMax[i - 1] * nums[i], nums[i]));
        max = Math.max(max, dpMax[i]);
    }
    return max;
}

再优化一下空间,利用一个变量表示dp[i-1]

public int maxProduct(int[] nums) {
    int n = nums.length;
    if (n == 0) {
        return 0;
    }
    int dpMax = nums[0];
    int dpMin = nums[0];
    int max = nums[0];
    for (int i = 1; i < n; i++) {
        //更新 dpMin 的时候需要 dpMax 之前的信息,所以先保存起来
        int preMax = dpMax;
        dpMax = Math.max(dpMin * nums[i], Math.max(dpMax * nums[i], nums[i]));
        dpMin = Math.min(dpMin * nums[i], Math.min(preMax * nums[i], nums[i]));
        max = Math.max(max, dpMax);
    }
    return max;
}

解法二

如果给定的数组全部都是正数,那么子数组最大的乘积是把所有的数字相乘。
如果出现了偶数个负数,则也是把所有的数字相乘。

问题转化为奇数个负数时如何处理,即下面两种情况

① 不包含最后一个负数的子数组。
在这里插入图片描述
② 不包含第一个负数的子数组。
在这里插入图片描述

public int maxProduct(int[] nums) {
    if (nums.length == 0) {
        return 0;
    }
    int max = 1;
    int res = nums[0];
    //包含了所有数相乘的情况    
    for (int i = 0; i < nums.length; i++) {//奇数个负数的情况一,正向遍历
        max *= nums[i];
        res = Math.max(res, max);
    }
    max = 1;
    
    for (int i = nums.length - 1; i >= 0; i--) {//奇数个负数的情况二,反向遍历
        max *= nums[i];
        res = Math.max(res, max);
    }
    return res;
}

但是如果遍历中出现了元素0,则到 0 的位置之后 max 就一直变成 0 了。

解决方法:把数组看成几个都不含有 0 的子数组。即在遇到零的时候,把 max 再初始化为 1 。

在这里插入图片描述

public int maxProduct(int[] nums) {
    if (nums.length == 0) {
        return 0;
    }

    int max = 1;
    int res = nums[0];
    for (int i = 0; i < nums.length; i++) {
        max *= nums[i];
        res = Math.max(res, max);
        if (max == 0) {
            max = 1;
        }

    }
    max = 1;
    for (int i = nums.length - 1; i >= 0; i--) {
        max *= nums[i];
        res = Math.max(res, max);
        if (max == 0) {
            max = 1;
        }
    }

    return res;
}
发布了82 篇原创文章 · 获赞 7 · 访问量 4991

猜你喜欢

转载自blog.csdn.net/magic_jiayu/article/details/104321781