【LeetCode】Maximum Product Subarray 求连续子数组使其乘积最大

Add Date 2014-09-23算法

Maximum Product Subarray数组

Find the contiguous subarray within an array (containing at least one number) which has the largest product.微信

For example, given the array [2,3,-2,4],
the contiguous subarray [2,3] has the largest product = 6.spa

简单来讲就是在一个 int 数组中找一段连续的子数组,使其乘积最大,数组中包含正数、负数和0。不考虑乘积太大超出范围。code

解法一:blog

动态规划的方法,今天在微信“待字闺中”中看到的,借来分享。遍历一遍,优于解法二,复杂度O(n).递归

 1 int max2(int a, int b) {
 2     if(a > b) return a;
 3     return b;
 4 }
 5 
 6 int max3(int a, int b, int c) {
 7     return max2(max2(a, b), c);
 8 }
 9 
10 int min2(int a, int b) {
11     if(a > b) return b;
12     return a;
13 }
14 
15 int min3(int a, int b, int c) {
16     return min2(min2(a, b), c);
17 }
18 
19 int maxProduct(int A[], int n) {
20     int max = A[0];
21     int min = A[0];
22     int a = max;
23     for(int i = 1; i < n; ++i) {
24         int tmpMax = max * A[i];
25         int tmpMin = min * A[i];
26         max = max3(tmpMax, tmpMin, A[i]);
27         min = min3(tmpMax, tmpMin, A[i]);
28         a = max2(max, a);
29     }
30     return a;
31 }

 

解法二:leetcode

以前作过找连续子数组使其加和最大,比较简单,见《剑指offer》Q31。get

刚开始也试图从那道题中找思路,发现不太科学…后来本身摸索出一个思路,分享一下。it

首先,任何一个数字和0相乘获得的都是0;另外,不考虑0的状况下,由于数字都是整数,因此乘积的绝对值是不会变小的;再次,负负得正。

基于这三点考虑,首先基于递归的思想用0分段,也就是说,把数组 A 拆分为 [一段不包含0的数组]、[0]、[剩下的数组],并设 rel1 为不包含0的子数组获得的最大乘积,rel2 为剩下的数组获得的最大乘积,那么,数组 A 的最大乘积就是 max{rel1,0,rel2}。rel2递归的用这个思路获得。

而后,就是求一个不包含0的数组的最大乘积。因为负负得正,能够想到,若是负数的个数为偶数,那么全部的数字相乘就是那个最大乘积;若是负数的个数为奇数,那么必定是某一个负数的左边全部数字乘积或者右边全部数字乘积为最大,因此就能够从前向后遍历数组,并把全部元素相乘,用 numAll1 记录这个乘积,同时每次更新 num1 为从前到后相乘过程当中最大的乘积;而后再从后向前遍历数组,用 numAll2 记录乘积,num2 记录最大值,这样整个数组的最大乘积就是 max{num1,num2}。

若是你以为不理解为何最大乘积的子数组必定是从第一个数字连续的一个子数组,或者是从最后一个数字连续的子数组,能够这样想:

若是这个乘积最大的子数组是中间的某一段,固然排除掉只有一个元素且为负数的状况,那么这个乘积必定是正数(这个还不懂的话本身想一下吧,简单的),若是

1.这段子数组左边(右边)是全正的,那么和左边(右边)全部的数相乘的结果必定不会比当前的结果小,因此能够从左边(右边)连续;因此这段子数组左边和右边必定都有负数!

2.这段子数组左边(右边)有偶数个负数,同1是不可能的;并且若是有多个负数,必定能够有偶数个能够包含在中间这段子数组中使乘积更大;因此必定左边和右边各有一个负数!

3.若是左边和右边各有一个负数,那么就有两个负数,这样的话把整个数组相乘的结果必定不小于中间这段子数组。

这个算法中遍历整个数组三次,复杂度为O(n).

终于把逻辑说完了,我的以为很啰嗦,只是想说明白点,不知道你们有没明白,下面附 code,欢迎其它思路。

 1 class Solution {
 2 public:
 3     int maxProductNo0(int A[], int n) {                 //没有0的数组求最大乘积
 4         int num1 = A[0];
 5         int numAll1 = A[0];
 6         for(int i = 1; i < n; ++i) {
 7             numAll1 *= A[i];
 8             num1 = numAll1 > num1 ? numAll1 : num1;
 9         }
10         
11         int num2 = A[n-1];
12         int numAll2 = A[n-1];
13         for(int i = n-2; i >= 0; --i) {
14             numAll2 *= A[i];
15             num2 = numAll2 > num2 ? numAll2 : num2;
16         }
17         return num1 > num2 ? num1 : num2;
18     }
19     
20     int maxProduct(int A[], int n) {                    //求数组最大乘积
21         if(A == NULL || n < 1)
22             return 0;
23         int index0 = 0;
24         int rel1 = A[0];
25         int rel2 = A[0];
26         bool have0 = false;
27         for(; index0 < n; ++index0) {
28             if(A[index0] == 0) {
29                 have0 = true;
30                 break;
31             }
32         }
33         if(index0 > 0)
34             rel1 = maxProductNo0(A, index0);            //没有0的数组的最大乘积
35         if(n-index0-1 > 0)
36             rel2 = maxProduct(A+index0+1, n-index0-1);  //剩下的数组的最大乘积
37         rel1 = rel1 > rel2 ? rel1 : rel2;
38         if(have0)
39             rel1 = rel1 > 0 ? rel1 : 0;
40         return rel1;
41     }
42 };
相关文章
相关标签/搜索