【LeetCode-动态规划】乘积最大子数组

题目描述

给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字)。
示例:

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

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

思路

因为是求乘积,而且数组中正负数都有,所以会出现最大值乘以负数变成最小值以及最小值乘以负数变成最大值的情况,所以我们要维护两个dp数组:dpMax和dpMin,dpMax[i]为以i为结尾的最大的序列乘积,dpMin[i]为以i为结尾的最小的序列乘积。分两种情况分析:

  • 当nums[i]>=0时:
    • dpMax[i] = max(dpMax[i-1]*nums[i], nums[i]);
    • dpMin[i] = min(dpMin[i-1]*nums[i], nums[i]);
  • 当nums[i]<0时:
    • dpMax[i] = max(dpMin[i-1]*nums[i], nums[i]);
    • dpMin[i] = min(dpMax[i-1]*nums[i], nums[i]);

综上,有:

  • dpMax[i] = max(dpMax[i-1]*nums[i], nums[i], dpMin[i-1]*nums[i]);
  • dpMin[i] = min(dpMin[i-1]*nums[i], nums[i], dpMax[i-1]*nums[i]);

对应代码如下:

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        if(nums.empty()) return 0;

        int dpMax[nums.size()], dpMin[nums.size()];
        memset(dpMax, 0, sizeof(dpMax));
        memset(dpMin, 0, sizeof(dpMin));
        int maxPro = nums[0];
        dpMax[0] = nums[0];
        dpMin[0] = nums[0];
        for(int i=1; i<nums.size(); i++){
            dpMax[i] = max(max(dpMax[i-1]*nums[i], nums[i]), dpMin[i-1]*nums[i]);
            dpMin[i] = min(min(dpMax[i-1]*nums[i], nums[i]), dpMin[i-1]*nums[i]);
            maxPro = max(dpMax[i], maxPro);
        }
        return maxPro;
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

空间复杂度优化:
可以不使用dp数组,只使用两个变量。代码如下:

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        if(nums.empty()) return 0;

        int curMax = nums[0];
        int curMin = nums[0];
        int maxPro = nums[0];
        for(int i=1; i<nums.size(); i++){
            int temp = curMax;    // 因为curMax在下一行可能会被更新,所以保存下来
            curMax = max(max(curMax*nums[i], nums[i]), curMin*nums[i]);
            curMin = min(min(curMin*nums[i], nums[i]), temp*nums[i]);
            maxPro = max(curMax, maxPro);
        }
        return maxPro;
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)
posted @ 2020-04-20 17:44  Flix  阅读(970)  评论(0编辑  收藏  举报