手撸代码系列(十五)--乘积最大子数组

本文介绍了一种解决乘积最大子数组问题的动态规划算法,通过实例详细解析了算法思路,包括如何定义状态转移方程,以及如何利用dpMax和dpMin两个数组来更新最大和最小乘积。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

示例 2:
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
题目分析:动态规划

     我们先定义一个数组 dpMaxdpMaxdpMax,用 dpMax[i]dpMax[i]dpMax[i] 表示以第 iii 个元素的结尾的子数组,乘积最大的值,也就是这个数组必须包含第 iii 个元素。

那么 dpMax[i]dpMax[i]dpMax[i] 的取值情况有以下几种:

  1. nums[i]≥0nums[i] \ge 0nums[i]0 并且 dpMax[i−1]≥0dpMax[i - 1] \ge 0dpMax[i1]0dpMax[i]=dpMax[i−1]∗nums[i]dpMax[i] = dpMax[i - 1] * nums[i]dpMax[i]=dpMax[i1]nums[i]

  2. nums[i]≥0nums[i] \ge 0nums[i]0 并且 dpMax[i−1]<0dpMax[i - 1] < 0dpMax[i1]<0,此时如果和前边的数累乘的话,结果会负得更多(即值越小),所以直接将nums[i]nums[i]nums[i] 赋值给当前的结果, dpMax[i]=nums[i]dpMax[i] = nums[i]dpMax[i]=nums[i]

  3. nums[i]<0nums[i] < 0nums[i]<0,如果前面累乘结果(dpMax[i−1]dpMax[i - 1]dpMax[i1]) 是一个负很多的数(即很小的负数), 和当前负数累乘的话(dpMax[i−1]∗nums[i]dpMax[i - 1] * nums[i]dpMax[i1]nums[i])就会变成一个更大的正数。因此,还需要一个数组 dpMindpMindpMin 来记录以第 iii 个元素的结尾的子数组,乘积最小的值。

    dpMin[i−1]<0dpMin[i - 1] < 0dpMin[i1]<0dpMax[i]=dpMin[i−1]∗nums[i]dpMax[i] = dpMin[i - 1] * nums[i]dpMax[i]=dpMin[i1]nums[i]
    dpMin[i−1]≥0dpMin[i - 1] \ge 0dpMin[i1]0dpMax[i]=nums[i]dpMax[i] = nums[i]dpMax[i]=nums[i]

dpMindpMindpMin的求解其实和上边求 dpMaxdpMaxdpMax 的过程其实是一样的。

我们注意到上边 dpMax[i]dpMax[i]dpMax[i] 的取值无非就是三种,dpMax[i−1]∗nums[i]dpMax[i - 1] * nums[i]dpMax[i1]nums[i]dpMin[i−1]∗nums[i]dpMin[i - 1] * nums[i]dpMin[i1]nums[i]以及 nums[i]nums[i]nums[i]

实际计算的时候,只需要从三个取值中选一个最大的即可:dpMax[i]=max⁡(dpMax[i−1]∗nums[i],dpMin[i−1]∗nums[i],nums[i])dpMax[i] = \max (dpMax[i - 1] * nums[i],dpMin[i - 1] * nums[i],nums[i])dpMax[i]=max(dpMax[i1]nums[i],dpMin[i1]nums[i],nums[i])

dpMindpMindpMin 同理:
dpMin[i]=min⁡(dpMax[i−1]∗nums[i],dpMin[i−1]∗nums[i],nums[i])dpMin[i] = \min (dpMax[i - 1] * nums[i],dpMin[i - 1] * nums[i],nums[i])dpMin[i]=min(dpMax[i1]nums[i],dpMin[i1]nums[i],nums[i])

Java代码:
/**
 * 【题目】: 乘积最大子数组
 * 给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子
 * 数组中至少包含一个数字),并返回该子数组所对应的乘积。
 * 
 * 示例 1:
 * 输入: [2,3,-2,4]
 * 输出: 6
 * 解释: 子数组 [2,3] 有最大乘积 6。
 * 
 * 示例 2:
 * 输入: [-2,0,-1]
 * 输出: 0
 * 解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。给定一个单链表,编写
一个函数返回该链表的中间点。
 */

public class leetcode_152 {
    /**
     * 动态规划求解 乘积最大的子数组
     * @param nums
     * @return
     */
    public static int maxProduct(int[] nums){
        int n = nums.length;
        if (n==0){
            return 0;
        }
        int[] dpMax = new int[n];
        dpMax[0] = nums[0];
        int[] dpMin = new int[n];
        dpMin[0] = nums[0];
        int max = nums[0];
        for (int i = 1; i < n; i++) {
            dpMax[i] = Math.max(dpMax[i-1]*nums[i], Math.max(dpMin[i-1]*nums[i], nums[i]));
            dpMin[i] = Math.min(dpMax[i-1]*nums[i], Math.min(dpMin[i-1]*nums[i], nums[i]));
            max = Math.max(max, dpMax[i]);
        }
        return max;
    }
    
    public static void main(String[] args) {
        int[] array = {1,4,-6,7,-2,-5, 3,-8,9};
        System.out.println(maxProduct(array));
    }
}

【注】
(1):leetcode 等平台只要我们完成一个函数即可,本人初出茅庐,为了巩固基本知识,故自己补充了部分代码,用于练手。本代码也许存在漏洞,望高手赐教。感谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值