nineloong'sblog
首页归档照片墙音乐杂谈友链关于
封面
点击查看全图

乘积最大子数组

2026/9/21
# hot100
# 动态规划

题目描述

LeetCode 152 — 乘积最大子数组 (Medium)

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

示例 1:

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

示例 2:

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

示例 3:

  • 输入:nums = [-2,3,-4]
  • 输出:24
  • 解释:整个数组的乘积 (-2) × 3 × (-4) = 24 是最大的。

约束:

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

求解思路

这道题和最大子数组和(LeetCode 53)长得很像,但乘积引入了一个加法没有的麻烦:负数。两个负数相乘会变成正数,这意味着一个当前看起来很小的负数,碰上后面一个负数后可能变成很大的正数。如果只维护以 nums[i] 结尾的最大乘积,遇到负数时会丢掉关键信息。

既然如此,同时维护两个值就行了:以 nums[i] 结尾的子数组乘积的最大值 maxold 和最小值 minold。为什么要维护最小值?因为当前元素如果是负数,它乘以最小值反而可能变成最大值——一个很大的负数乘以一个很小的负数,结果是一个很大的正数。

转移时,新的最大值 maxnew 有三个候选:nums[i] 自己重新开始、maxold * nums[i] 接在之前的最大值后面、minold * nums[i] 接在之前的最小值后面(负负得正)。minnew 同理,取三者中的最小值。最后在整个遍历过程中记录出现过的最大乘积即可。

空间上,每个状态只依赖前一步,两个变量滚动更新就够了。


解法:动态规划

/*
需要维护并不断更新以nums[i]结尾的子数组乘积的最大值和最小值
maxnew = max(nums[i], maxold * nums[i], minold * nums[i])
minnew = min(nums[i], maxold * nums[i], minold * nums[i])
最后返回maxnew
*/
#include <vector>
#include <algorithm>

using namespace std;

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        if(nums.size() == 1) return nums[0];
        int maxold = nums[0];
        int minold = nums[0];
        int ans = nums[0];
        for(int i = 1; i < nums.size(); i++){
            int maxnew = max(nums[i], max(maxold * nums[i], minold * nums[i]));
            int minnew = min(nums[i], min(maxold * nums[i], minold * nums[i]));
            maxold = maxnew;
            minold = minnew;
            ans = max(ans, maxnew);
        }
        return ans;
    }
};

复杂度: 时间 O(n),一次遍历;空间 O(1),只用几个变量。


相关题目

  • LeetCode 53 — 最大子数组和
  • LeetCode 198 — 打家劫舍
  • LeetCode 713 — 乘积小于 K 的子数组
  • LeetCode 1049 — 最后一块石头的重量 II
avatar

nineloong

一隅之地,深耕自我

RECOMMENDED

二分查找模版总结

2026/5/23

每日一题:检查数组是否经排序和轮转得到

2026/5/23

零钱兑换

2026/8/25

Table of Contents