Leetcode(152) 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.

分析思路:

一开始看这题目的样子以为要用动态规划,但是完全不好写动态规划的方程。其实,题目中的输入可以这么来看,

如果输入数组中一个0都没有,那么就把所有的输入全部相乘得到一个结果,如果这个结果为>0,必然是largest product,如果结果<0,那么就从输入数组的左边开始向右扫描,直到找到第1个负数为主,那么此时去掉左边的subarray(从输入数组的第1个开始,到第1个负数为止的子数组。例如,[-2, 2, 3, -3]的左subarray为[-2], [2, -2, 3, -3]的左subarray为[2, -2])后,去掉左边的subarray后剩下数的乘积必然为正数。同样的过程按照从右到左的顺序对右边的数组走一遍,得到去掉右边subarray后剩下数的乘积,也必然为正数。比较两个正数,较大者为所求。把上面的过程放入辅助函数subsetMax中。

如果输入数组中有0,就把0作为分界线,得出一些子数组,对每个子数组做上面的事情。在每个子数组得到的最大乘积中选择最大的。

比较特殊的情况是 当输入数组为[-1],此时需要在subsetMax中特殊处理一下。

这道题目也是个非常具有面试题目感觉的题目,没有任何算法纯代码,但是代码很有区分度。
代码如下:

class Solution {
    
private:
    int subsetMax(int sub[], int n) {
        if (n == 1) return sub[0];
        int subsetProduct = 1;
        int posStart = -1;
        int posEnd = -1;
        for (int i = 0; i < n ; ++i)
            subsetProduct *= sub[i];
        if (subsetProduct > 0) return subsetProduct;
        int leftSubsetProduct = 1;
        int rightSubsetProduct = 1;
        for (posStart = 0; posStart < n ; ++posStart)
            if (sub[posStart] < 0) break;
        for (int i = posStart + 1; i < n; ++i)
            leftSubsetProduct *= sub[i];
        for (posEnd= n-1; posEnd >= 0 ; --posEnd)
            if (sub[posEnd] < 0) break;
        for (int i = posEnd - 1; i >= 0; --i)
            rightSubsetProduct *= sub[i];
        return leftSubsetProduct > rightSubsetProduct ? leftSubsetProduct:rightSubsetProduct;
    }
    
public:
    
    int maxProduct(int A[], int n) {
        int arrayMax = INT_MIN;
        int subStart = 0;
        int subEnd = 0;
        int curMax = INT_MIN;
        bool hasZero = false;
        while (subStart < n) { // 简化了while (subStart < n && subEnd < n) 因为while{}内部的最后一步是subStart = subEnd + 1;
            while (subStart < n && A[subStart]==0) {
                hasZero = true;
                subStart++;
            }
            subEnd = subStart;
            while (subEnd < n && A[subEnd]!=0)
                subEnd++;
            if (subEnd < n)
                hasZero = true;
            if (subStart < subEnd) {
                curMax = subsetMax(A + subStart, subEnd - subStart);
                if (curMax > arrayMax) arrayMax = curMax;
            }
            subStart = subEnd + 1;
        }
        if (hasZero && arrayMax < 0) return 0;
        else return arrayMax;
    }
};

update 2014-11-23

更简洁的思路是动态规划。解答思路来自官网的答案解析。

Let us denote that:

f(k) = Largest product subarray, from index 0 up to k. Similarly,

g(k) = Smallest product subarray, from index 0 up to k.  Then,

f(k) = max( f(k-1) * A[k], A[k], g(k-1) * A[k] )
g(k) = min( g(k-1) * A[k], A[k], f(k-1) * A[k] )


There we have a dynamic programming formula. Using two arrays of size n, we could deduce the final answer as f(n-1).

class Solution {
public:
    int maxProduct(int A[], int n) {
        if (n == 0) return 0;
        if (n == 1) return A[0];
        int result = A[0]; //NOTE: 不能初始化为resutl = INT_MIN,因为已经用了A[0]作为基准了。
        int min_product = A[0];
        int max_product = A[0];
        for (int i = 1; i < n; ++i) {
            int org_min = min_product; //NOTE: data dependency: 原始的min max值需要保留下来。
            int org_max = max_product; //NOTE: data dependency: 原始的min max值需要保留下来。
            max_product = ((org_max * A[i]) > (org_min * A[i]))?(org_max * A[i]):(org_min * A[i]);
            max_product = (max_product > A[i]) ? max_product:A[i];
            min_product = ((org_max * A[i]) < (org_min * A[i]))?(org_max * A[i]):(org_min * A[i]);
            min_product = (min_product < A[i]) ? min_product:A[i];
            if (max_product > result)
                result = max_product;
            //std::cout<<"i = "<<i<<", max="<<max_product<<", min="<< min_product<<" ,res="<<result<<std::endl;
        }
        return result;
    }
};



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值