算法

LeetCode题解:乘积最大子数组

2022-03-29  本文已影响0人  搬码人

题目描述

给你一个整数数组nums,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
测试用例的答案是一个32位整数。
子数组是数组的连续子序列。

示例

思路方法

看到这道题,我相信很多的小伙伴都想到了前面的“最大子数组”的问题,而且肯定第一反应是使用“最大子数组”的解法去解决这道题。没错,小编刚开始也是这么想的,但是,这就错了!
因为这道题不满足“最大子数组”的最优子结构。
具体地讲,如果a={5,6,−3,4,−3},那么此时 fmax对应的序列是 {5,30,−3,4,−3},按照前面的算法我们可以得到答案为 30,即前两个数的乘积,而实际上答案应该是全体数字的乘积。我们来想一想问题出在哪里呢?问题出在最后一个−3 所对应的 fmax的值既不是 −3,也不是4×−3,而是5×30×(−3)×4×(−3)。所以我们得到了一个结论:当前位置的最优解未必是由前一个位置的最优解转移得到的。
我们可以从正负性进行讨论
考虑当前位置如果是一个负数的话,那么我们希望以它前一个位置结尾的某个段的积也是个负数,这样就可以负负得正,并且我们希望这个积尽可能「负得更多」,即尽可能小。如果当前位置是一个正数的话,我们更希望以它前一个位置结尾的某个段的积也是个正数,并且希望它尽可能地大。于是这里我们可以再维护一个 fmin(i),它表示以第 i 个元素结尾的乘积最小子数组的乘积,那么我们可以得到这样的动态规划转移方程:

image.png
它代表第 i 个元素结尾的乘积最大子数组的乘积 fmax(i),可以考虑把ai加入第 i−1 个元素结尾的乘积最大或最小的子数组的乘积中,二者加上ai,三者取大,就是第i个元素结尾的乘积最大子数组的乘积。第 i 个元素结尾的乘积最小子数组的乘积 fmin(i) 同理。
class Solution {
    public int maxProduct(int[] nums) {
        int length = nums.length;
        int[] maxF = new int[length];
        int[] minF = new int[length];
        System.arraycopy(nums,0,maxF,0,length);
        System.arraycopy(nums,0,minF,0,length);
        for(int i=1;i<length;i++){
            maxF[i] = Math.max(nums[i]*maxF[i-1],Math.max(nums[i],nums[i]*minF[i-1]));
            minF[i] = Math.min(nums[i]*minF[i-1],Math.min(nums[i],nums[i]*maxF[i-1])); 
        }
        int result = maxF[0];
        for(int i=1;i<length;i++){
            result = Math.max(maxF[i],result);
        }
        return result;
    }
}

复杂度分析

优化空间

class Solution {
    public int maxProduct(int[] nums) {
        int maxF=nums[0],minF=nums[0],result=nums[0];
        int length = nums.length;
        for(int i=1;i<length;i++){
            int max = maxF,min = minF;
            maxF = Math.max(max*nums[i],Math.max(nums[i],min*nums[i]));
            minF = Math.min(min*nums[i],Math.min(nums[i],max*nums[i]));
            result = Math.max(result,maxF);
        }
        return result;
    }
}   
上一篇下一篇

猜你喜欢

热点阅读