力扣152——乘積最大子序列

這道題主要就是利用動態規劃進行解答,若是要進行優化,就須要找規律了。
<!-- 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的解法,看來能夠繼續優化。

找規律

咱們設想一下,若是這個整數數組只有正數,那麼最大值就只須要將全部數字相乘便可。

若是包含負數,那麼須要分紅兩種狀況:

  1. 負數爲偶數個,由於負負得正,因此依舊將全部數字相乘便可。
  2. 負數爲奇數個,要麼從前日後乘到最後一個負數以前,要麼從後往前乘到第一個負數以前。

若是包含 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。這個方法真的是又快又省空間,只是須要咱們耐心尋找其中的規律。

總結

以上就是這道題目個人解答過程了,不知道你們是否理解了。通常來講利用動態規劃就夠了,若是想繼續優化,就須要尋找其中的規律了。

有興趣的話能夠訪問個人博客或者關注個人公衆號、頭條號,說不定會有意外的驚喜。

https://death00.github.io/

公衆號:健程之道

相關文章
相關標籤/搜索