这道题主要就是利用动态规划进行解答,若是要进行优化,就须要找规律了。
<!-- more -->git
给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。github
示例 1:segmentfault
输入: [2,3,-2,4] 输出: 6 解释: 子数组 [2,3] 有最大乘积 6。
示例 2:数组
输入: [-2,0,-1] 输出: 0 解释: 结果不能为 2, 由于 [-2,-1] 不是子数组。
原题url:https://leetcode-cn.com/probl...优化
看到这道题,第一眼想到的就是暴力求解,从第一个数字开始,一直连续着求到最后。稍微增长了对于 0 的判断,由于 0 乘以任何数都等于 0,因此只要碰到 0,当前的此次求解就能够中止。让咱们看看代码:url
class Solution { int max = Integer.MIN_VALUE; public int maxProduct(int[] nums) { for (int i = 0; i < nums.length; i++) { if (nums[i] == 0) { if (max < 0) { max = 0; } continue; } dfs(nums, i + 1, nums[i]); } return max; } public void dfs(int[] nums, int index, int total) { // 当前乘积是否最大 if (total > max) { max = total; } // 有没有越界 if (index >= nums.length) { return; } // 当前数字是不是0,是0的话就没有必要继续下去,由于乘积永远为0 if (nums[index] == 0) { return; } dfs(nums, index + 1, total * nums[index]); } }
提交以后,报超出时间限制
。看来暴力求解果真不可取,让咱们再想一想。spa
既然不能暴力求解,那咱们能不能利用上以前求解的结果呢?没错,这就是动态规划
了。code
本来想着是逐个求出当前下标下的最大值,但由于是乘积,考虑到负负得正的状况,只记录最大值可能还不够,须要最大值和最小值一块儿记录。内存
但根据以前优化的经验,并不须要申请额外的数组存储最大值和最小值,只须要用常数量的空间存储以前的结果,由于题目要求的是连续,只须要记录上一个序号的结果就够了。leetcode
接下来看看代码:
class Solution { public int maxProduct(int[] nums) { int n = nums.length; if (n == 0) { return 0; } // 包含上一个位置的数,得出来的最大值和最小值 int dpMax = nums[0], dpMin = nums[0]; // 最终结果的最大值 int max = nums[0]; // 遍历求解 for (int i = 1; i < n; i++) { // 更新 dpMin 的时候须要 dpMax 以前的信息,因此先保存起来 int preMax = dpMax; // 求出 (dpMin * nums[i])、(dpMax * nums[i])、nums[i] 这三个数的最大值和最小值 dpMax = Math.max(dpMin * nums[i], Math.max(dpMax * nums[i], nums[i])); dpMin = Math.min(dpMin * nums[i], Math.min(preMax * nums[i], nums[i])); // 更新最终的最大值 max = Math.max(max, dpMax); } return max; } }
提交OK,执行用时:2 ms
,内存消耗:38.1 MB
。但彷佛还有稳定耗时只要1 ms
的解法,看来能够继续优化。
咱们设想一下,若是这个整数数组只有正数,那么最大值就只须要将全部数字相乘便可。
若是包含负数,那么须要分红两种状况:
若是包含 0,那么依旧只须要从前日后和从后往前各乘一遍,只是在遇到 0 的时候,将以前相乘所获得的结果置为 1 便可,这样就能够达到单独计算中间数字连续相乘
的效果。
根据上面的规律,其实就是从后往前、从前日后,各乘一遍,找出最大结果便可。接下来看看代码:
class Solution { public int maxProduct(int[] nums) { if (nums.length == 0) { return 0; } // 记录中间相乘的结果 int max = 1; // 记录最终的结果 int res = nums[0]; // 从前日后乘一遍 for (int i = 0; i < nums.length; i++) { max *= nums[i]; res = Math.max(res, max); // 若是遇到 0,则将中间记录的结果置为 1 if (max == 0) { max = 1; } } max = 1; // 从后往前乘一遍 for (int i = nums.length - 1; i >= 0; i--) { max *= nums[i]; res = Math.max(res, max); // 若是遇到 0,则将中间记录的结果置为 1 if (max == 0) { max = 1; } } return res; } }
提交OK,执行用时:1 ms
,内存消耗:36.3 MB
。这个方法真的是又快又省空间,只是须要咱们耐心寻找其中的规律。
以上就是这道题目个人解答过程了,不知道你们是否理解了。通常来讲利用动态规划就够了,若是想继续优化,就须要寻找其中的规律了。
有兴趣的话能够访问个人博客或者关注个人公众号、头条号,说不定会有意外的惊喜。
公众号:健程之道