力扣【动态规划专题】152. 乘积最大子数组

289 阅读1分钟

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

题目链接

152. 乘积最大子数组 - 力扣(LeetCode)

题目描述

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

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

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

测试用例

用例1:

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

限制

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

题目分析

题目会给定一个元素取值在 [-10, 10] 的数组,需要我们求这个数组里,连续元素的最大乘积

由于数组的组成可能包含由正数、负数、零等任意元素,一个比较常规的思路为:使用一个栈,顺序遍历给定的数组:

  • 如果出现的元素为 负数,直接入栈;
  • 如果当前的元素为 正数,将其与栈顶的 正数 合并相乘(栈顶无正数则直接压入)
  • 如果当前的元素为 0,将栈另存为副本 arr,对 arr 进行求最大值,清空栈

arr 处理的思路如下:

  • 如果 arr 中存在有偶数个负数,直接相乘即可
  • 如果 arr 中出现的负数个数为 奇数, 我们将 arr 划分为: 舍弃第一个负数左边的数组,舍弃最后一个负数右边的数组,分别求他们的乘积,选取其中最大值

代码实现

完整的代码实现如下

var maxProduct = function (nums) {
    // 尽量合并相邻的正数,遇到 0 ,计算前面的最大值
    let stack = [];
    let max = nums[0], indexs = [];
    for (let i = 0; i < nums.length; i++) {
        let val = nums[i];
        if (val == 0) {
            caculate(stack);
            max = Math.max(0, max)
        } else if (val > 0) {
            stack.push(val * (stack[stack.length - 1] > 0 ? stack.pop() : 1));
        } else {
            stack.push(val);
            indexs.push(stack.length - 1);
        }
    }
    caculate(stack);
    return max;
    function maxFunc(arr) {
        if (arr.length > 0) max = Math.max(max, arr.reduce((a, b) => a * b))
    }
    function caculate(arr) {
        if (indexs.length % 2 == 0) {
            maxFunc(arr)
        } else {
            let left = indexs[0], right = indexs[indexs.length - 1];
            maxFunc(arr.slice(left + 1))
            maxFunc(arr.slice(0, right))
        }
        count = 0;
        indexs = [];
        stack=[];
    }
};

image.png