152.Maximum Product Subarray---dp---連續子數組的最大乘積---《編程之美》2.13子數組的最大乘積

題目連接:https://leetcode.com/problems/maximum-product-subarray/description/數組

題目大意:給出一串數組,找出連續子數組中乘積最大的子數組的乘積。ide

法一:暴力。居然能過,數據也太水了。兩個for循環,遍歷每個可能的連續子數組,找出最大值。代碼以下(耗時161ms):spa

 1     public int maxProduct(int[] nums) {
 2         int res = Integer.MIN_VALUE, len = nums.length;
 3         for(int i = 0; i < len; i++) {
 4             int sum = 1;
 5             for(int j = i; j < len; j++) {
 6                 sum *= nums[j];
 7                 res = Math.max(res, sum);
 8             }
 9         }
10         return res;
11     }
View Code

法二(借鑑):dp,利用兩個dp數組,dp_max[i]表示從0到i的數中,子數組乘積最大值,dp_min[i]表示從0到i的數中,子數組乘積最小值,而後每次都更新這兩個值,從dp_max數組中取的最大值便可。代碼以下(耗時4ms):code

 1     public int maxProduct(int[] nums) {
 2         int dp_max[] = new int[nums.length];
 3         int dp_min[] = new int[nums.length];
 4         int res = Integer.MIN_VALUE;
 5         dp_max[0] = nums[0];
 6         dp_min[0] = nums[0];
 7         for(int i = 1; i < nums.length; i++) {
 8             //更新最大值數組
 9             dp_max[i] = Math.max(Math.max(dp_max[i - 1] * nums[i], dp_min[i - 1] * nums[i]), nums[i]);
10             //更新最小值數組
11             dp_min[i] = Math.min(Math.min(dp_max[i - 1] * nums[i], dp_min[i - 1] * nums[i]), nums[i]);
12             res = Math.max(dp_max[i], res);
13         }
14         return res;
15     }
View Code

法三(借鑑):兩次遍歷,正向遍歷+反向遍歷,空間複雜度是o(1),遇0則歸1,不然儘管相乘,找到最大值。代碼以下(耗時1ms);blog

 1     public int maxProduct(int[] nums) {
 2         int res = Integer.MIN_VALUE, sum = 1;
 3         //正向遍歷,取最大值
 4         for(int i = 0; i < nums.length; i++) {
 5             sum *= nums[i];
 6             res = Math.max(res, sum);
 7             if(nums[i] == 0) {
 8                 sum = 1;
 9             }
10         }
11         //反向遍歷,取最大值
12         sum = 1;
13         for(int i = nums.length - 1; i >= 0; i--) {
14             sum *= nums[i];
15             res = Math.max(res, sum);
16             if(nums[i] == 0) {
17                 sum = 1;
18             }
19         }
20         return res;
21     }
View Code

法四(借鑑):下面這種方法也是用兩個變量來表示當前最大值和最小值的,可是沒有無腦比較三個數,而是對於當前的nums[i]值進行了正負狀況的討論:ip

1. 當遍歷到一個正數時,此時的最大值等於以前的最大值乘以這個正數和當前正數中的較大值,此時的最小值等於以前的最小值乘以這個正數和當前正數中的較小值。

2. 當遍歷到一個負數或0時,咱們先用一個變量t保存以前的最大值mx,而後此時的最大值等於以前最小值乘以這個負數和當前負數中的較大值,此時的最小值等於以前保存的最大值t乘以這個負數和當前負數中的較小值。leetcode

3. 在每遍歷完一個數時,都要更新最終的最大值。get

P.S. 若是這裏改爲求最小值的話,就是求最小子數組乘積。代碼以下(耗時3ms):
 1     public int maxProduct(int[] nums) {
 2         int res = nums[0], ma = nums[0], mi = nums[0];
 3         for(int i = 1; i < nums.length; i++) {
 4             if(nums[i] > 0) {
 5                 ma = Math.max(ma * nums[i], nums[i]);
 6                 mi = Math.min(mi * nums[i], nums[i]);
 7             }
 8             //注意負數或0的狀況
 9             else {
10                 int t = ma;
11                 ma = Math.max(mi * nums[i], nums[i]);
12                 mi = Math.min(t * nums[i], nums[i]);
13             }
14             res = Math.max(res, ma);
15         }
16         return res;
17     }
View Code

 法五(借鑑):在上面的解法中咱們分析了當nums[i]爲正數時,最大值和最小值的更新狀況,爲負數時,稍有不一樣的就是最小值更新時要用到以前的最大值,而不是更新後的最大值,因此咱們纔要用變量t來保存以前的結果。而下面這種方法的巧妙處在於先判斷一個當前數字是不是負數,是的話就交換最大值和最小值。那麼此時的mx就是以前的mn,因此mx的更新仍是跟上面的方法是統一的,而在在更新mn的時候,以前的mx已經保存到mn中了,並且並無改變,因此能夠直接拿來用。代碼以下(耗時1ms):io

 1     public int maxProduct(int[] nums) {
 2         int res = nums[0], ma = nums[0], mi = nums[0];
 3         for(int i = 1; i < nums.length; i++) {
 4             if(nums[i] <= 0) {
 5                 int t = ma;
 6                 ma = mi;
 7                 mi = t;
 8             }
 9             ma = Math.max(ma * nums[i], nums[i]);
10             mi = Math.min(mi * nums[i], nums[i]);
11             res = Math.max(ma, res);
12         }
13         return res;
14     }
View Code
相關文章
相關標籤/搜索