算法日刷:leetcode152 乘积最大子数组问题

本文介绍了如何利用动态规划解决LeetCode上的最大乘积子数组问题,包括两种解法:标准DP和滚动数组优化。通过实例展示了如何考虑符号影响,以及如何通过滚动数组减少空间复杂度。

问题描述

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

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

子数组 是数组的连续子序列。(连续子序列问题:就是序号是相互连着的:如 1、2、3、4)连续

(区分):元素连续以及相应的元素可删除,保证局部顺序有序的区别。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/maximum-product-subarray
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

第一联想到:53 最大子序和问题 (问题的描述相同,但是一个是乘积,一个是加合)

对于最大子序和问题,解法有两种:

一类是通过贪心的思路实现相应的输出。判断前一个值对下一个值的共享程度。

二:使用动态规划去记录下到子序列每一个结尾的和的情况。最后去探究最优解的值(寻找最大值)。最优解是:以第i个数结尾的子序列的和的最大情况

状态转移方程:dp[i] = Math.max(dp[i-1]+num[i],num[i])

借着该思路出发,进行同类迁移:使用动态规划的角度出发

使用乘法:还需要额外考虑一个点:符号问题 因此就需要考虑维护的是最大值以及最小值的问题。因为符号不同,就可以导致情况出现反转的现象。

具体代码的实现
        

class Solution {
    public int maxProduct(int[] nums) {
        // 子数组问题;动态规划实现(连续的子数组)
        int maxValue [] = new int [nums.length];
        int minValue [] = new int [nums.length];
        int dp[] = new int [nums.length];
        //dp[i] 表示以数组的第i 个数结尾的连续的子数组的最大乘积问题
        //初始化:每个结尾都可以考虑以本身为单个的值
            maxValue[0] = nums[0];
            minValue[0] = nums[0];
        int sum = 0;
        //遍历 (for 循环实现)
        for(int i = 1;i<nums.length;i++){
            // 值问题的选择上进行区分:不再是单纯的本身的值
          maxValue[i] =Math.max(nums[i]* maxValue[i-1],Math.max(nums[i],nums[i]*minValue[i-1]));
          minValue[i] =Math.min(nums[i]* maxValue[i-1],Math.min(nums[i],nums[i]*minValue[i-1]));
        }
        //从新遍历一遍;获取相应的最值
        int result = Integer.MIN_VALUE;
        for(int i = 0;i<nums.length;i++){
            result = Math.max(result,maxValue[i]);
        }
        return result;
    }
}

        对于情况的分类:不需要去考虑符合,而是去考虑他最值的情况:如当前的数为正数,则当前值* 前一个数的最大情况既可以满足。若当前值为负数,则当前值* 前一个数的最负数的情况(最小值)既可以满足。若为0的话,则就是其本身。

        同理:维护相应的最小值的情况。

思路二(基于上面的结论的演进)

        上述的算法实质上等同于维护了一个一维数组,用于保存其最大以及最小值,因此思考:是否有方法,能够对相应的空间压缩。因此联想到动态规划所学到的“滚动数组”。其思想就是将数组维护的最大值变成维护单个最值的问题。

carl哥:代码随想录中对滚动数组的描述

滚动数组:上一个数的最值都是用来维护和使用下一个的 ,

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

        总结:对于动态规划的理解需要加深:懂得相应的状态的转变以及相关的子问题的推解。对于同类型的题目(最大子序和)要进行相应的联想和类比推理

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值