本文目录
leetcode 21天动态规划入门——从0到0.5【Day06】乘积最大子数组数组 以及乘积为正的最大子数组长度 dp优化
DAY06
写在前面
今天是动态规划入门的第六天了,今天的题稍微有一丢丢难,需要考虑到相关的优化,如果觉得不适,那么就掌握最简单的那种方法就好啦,最近博主在学一些技术,这几天可能只会暂时更新每日一题以及动态规划入门这两个板块了,Java基础的知识可能会在下个月博主放假的时候再开始更新(一月7号前后)谢谢大家哦~同时小付也理解技术知识不光需要了解了基础知识之外,还需要拓展以后业务中可能出现的情况,小付会在之后一一改进,话不多说,冲就完事了!
题目
题目一
- 乘积最大子数组
给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组
(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
示例
示例1:
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例2:
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
思路
经过几天的动态规划训练,应该入门基础的思想了,
不过针对此题,之前的思想虽然还可以适用,但是已经快要无法满足
时间与空间的需求了,这有违背我们学习算法的初衷,
不过最简单的思想还是要会:
1)今天可不只能维护一个数组dp了,
因为你要考虑如果当前的乘积和是最小子数组乘积和时,
如果下一个数是负数再相乘时,此时就会得到一个相对较大的数值,
就要和最大的子数组乘积和相比较取最大值。
2)同理如果此时得到的是最大子数组乘积和时,
如果下一个数是负数再相乘时,此时又会得到一个相对较小的数值,
所以最后随数组滚动时需要不断进行比较,
就会导致时间以及空间复杂度大大提高。
动态转化方程:
用来获取当前乘积最大值:
dpMax[i] = Math.max(nums[i], Math.max(dpMax[i - 1] * nums[i], dpMin[i - 1] * nums[i]));
用来获取当前乘积最小值:
dpMin[i] = Math.min(nums[i], Math.min(dpMin[i - 1] * nums[i], dpMax[i - 1] * nums[i]));
代码实现
class Solution {
public int maxProduct(int[] nums) {
int res = nums[0];
int[] dpMax = new int[nums.length + 1], dpMin = new int [nums.length + 1];
dpMax[0] = nums[0]; // 初始化两个dp数组
dpMin[0] = nums[0];
for(int i = 1; i < nums.length; i++)
{
dpMax[i] = Math.max(nums[i], Math.max(dpMax[i - 1] * nums[i], dpMin[i - 1] * nums[i])); //获取乘积最大值
dpMin[i] = Math.min(nums[i], Math.min(dpMin[i - 1] * nums[i], dpMax[i - 1] * nums[i])); //获取乘积最小值
res = Math.max(res, dpMax[i]);
}
return res;
}
}
执行结果
还没我家小狗狗吃得少跑得快呢~
优化
既然上面思路都提到了因为维护数组消耗了大量空间时间,那么我们可以利用变量来模拟当前状况从而节省空间与时间
class Solution {
public int maxProduct(int[] nums) {
//设计imax、imin来获取当前子数组的最大值与最小值
int max = nums[0],imax = 1 ,imin = 1;
for (int i = 0 ; i< nums.length ;i++){
//如果当前需要乘的数<0那么最大将变成最小的 将其进行交换方便变量进行处理
if (nums[i] < 0){
int temp = imax ;
imax = imin;
imin = temp;
}
//如果当前需要乘的数<0那么最大将变成最小的 最小的变成最大的
imax = Math.max(imax*nums[i],nums[i]);
imin = Math.min(imin*nums[i],nums[i]);
max = Math.max(imax,max);
}
return max;
}
}
优化结果
题目二
示例
示例1:
输入:nums = [1,-2,-3,4]
输出:4
解释:数组本身乘积就是正数,值为 24 。
示例2:
输入:nums = [1,-2,-3,4]
输出:4
解释:数组本身乘积就是正数,值为 24 。
示例3:
输入:nums = [-1,-2,-3,0,1]
输出:2
解释:乘积为正数的最长子数组是 [-1,-2] 或者 [-2,-3] 。
示例4:
输入:nums = [-1,2]
输出:1
提示
1 <= nums.length <= 10^5
-10^9 <= nums[i] <= 10^9
思路
这道题需要进行分类讨论
1)当前需要进行乘的值如果等于0则正负最长子数组长度均需重新置零
重新从下一个数进行遍历
2)当此时乘数为正时,此时维护的这个正子数组和长度+1
且需要为下次迎接乘数为负数做准备,使得负数长度也+1
3)如果当前乘数为负数,则需要先记录当前正数的子数组长度,
此时正数子数组长度需要为下一次迎接负数做准备+1
代码实现
class Solution {
public int getMaxLen(int[] nums) {
//维护当前正数子数组长度与负数子数组长度
int posLen = 0;
int negLen = 0;
int max = 0;
//依次遍历分类讨论
for (int i : nums){
if (i == 0){
posLen = 0;
negLen = 0;
continue;
}
//当前乘数大于0时
if (i > 0){
posLen++;
negLen = negLen == 0? 0:negLen+1;
}
//当前乘数小于0时
if (i < 0){
int temp = posLen;
posLen = negLen == 0 ? 0 :negLen+1;
negLen = temp+1;
}
//滚动变量用来记录最大值
max = Math.max(max,posLen);
}
return max;
}
}
执行结果
写在最后
今日的动态规划相较于昨天已经有了很大的改变,
不能再单一想通过一个数组或者变量来维护所需求解的值了,
也需要通过数学的相关知识来分类讨论进行题解
坚持打卡第六天~
加油 ~
最后
每天进步点 每天收获点
愿诸君 事业有成 学有所获
如果觉得不错 别忘啦一键三连哦~