乘积最大子数组

162 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

一、题目

LeetCode 乘积最大子数组

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

测试用例的答案是一个 32-位 整数。

子数组 是数组的连续子序列。

示例 1:

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

示例 2:

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

提示:

1 <= nums.length <= 2 * 104
-10 <= nums[i] <= 10
nums 的任何前缀或后缀的乘积都 保证 是一个 32-位 整数

二、题解

简单的说就是需要求一个数组中,所有可能的连续子数组中元素乘积最大的一个值。据此可以很容易的想到求出数组所有的子数组,然后再计算对应的乘积值,保留最大的一个即可,但是如果数组比较大或者元素比较多的情况下,可能就没那个条件实现了。

方法一

对此可以使用动态规划来解决,首先定义一个同等大小的数组dpMax,那么dpMax[i]就表示nums数组中以i位置结尾的子数组中的最大乘积值,需要注意这个子数组中元素包含i位置元素。那么说明dpMax[i - 1]的最大乘积的子数组也包含第i - 1位置的元素,i - 1位置与i位置是连续的,所以dpMax[i]的子数组最大乘积值就可能是dpMax[i - 1]的值乘上nums[i]的值,或者是nums[i]的本身的值。同时元素值可能是负数,负数如果与正数相乘那么就变成了更小的负数了,但如果两个最小的负数相乘那就变成了最大的正数了。所以还需要定义一个数组dpMin,那dpMin[i]就是nums数组中以i位置结尾的子数组中的最小乘积值,我们期望遇到一个更小的数,这样相乘就可能得到一个更大的数。最终dpMax[i]就取前一个dpMax[i - 1] * nums[i]dpMin[i - 1] * nums[i]nums[i]三者中最大的一个;dpMin[i]就取前一个dpMin[i - 1] * nums[i]dpMax[i - 1] * nums[i]nums[i]三者中最小的一个。最后记录下dpMax中最大的乘积返回即可。同时对于dp数组我们只会使用到dp[i - 1],所以可以直接把dp数组替换为一个变量来记录就好了。

三、代码

方法一 Java代码

class Solution {
    public int maxProduct(int[] nums) {
        int dpMax = nums[0];
        int dpMin = nums[0];
        int product = nums[0];
        for (int i = 1; i < nums.length; ++i) {
            int max = dpMax * nums[i];
            int min = dpMin * nums[i];
            dpMax = Math.max(max, Math.max(nums[i], min));
            dpMin = Math.min(min, Math.min(nums[i], max));
            product = Math.max(dpMax, product);
        }
        return product;
    }
}

时间复杂度:O(n),需要遍历一次数组。

空间复杂度:O(1),可以单使用个遍历代替dp数组,那就只需要常数的空间。