這道題主要就是利用動態規劃進行解答,若是要進行優化,就須要找規律了。
<!-- 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
。這個方法真的是又快又省空間,只是須要咱們耐心尋找其中的規律。
以上就是這道題目個人解答過程了,不知道你們是否理解了。通常來講利用動態規劃就夠了,若是想繼續優化,就須要尋找其中的規律了。
有興趣的話能夠訪問個人博客或者關注個人公衆號、頭條號,說不定會有意外的驚喜。
公衆號:健程之道