小小程序媛  
得之坦然,失之淡然,顺其自然,争其必然

题目

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.

Subscribe to see which companies asked this question

分析

最大字段积问题,之前我们熟悉的题目是求最大字段和。其实,他们本质是相同的,应该属于动态规划的范畴。

方法一(遗憾的TLE):
由于最终的最大乘积结果可能是从0~size-1的任何一个位置开始的子数组,所以我们可以先求出从0~siz-1开始的每个位置到最终位置的最大乘积,保存起来。

然后遍历该最大乘积数组,找出其中的最大值。

该方法复杂度有O(n^2)吧,遗憾的是,TLE了。。。

方法二:
不得不再次思考效率高的算法:

我们知道对序列元素遍历到 i 时,其值可正可负;
最大值可能是:
1. 前面子序列最大正乘积 * 正值;
2. 前面子序列最小负乘积 * 负值;

也就是说:其实子数组乘积最大值的可能性为:累乘的最大值碰到了一个正数;或者,累乘的最小值(负数),碰到了一个负数。所以每次要保存累乘的最大(正数)和最小值(负数)。
同时,还有一个选择起点的逻辑,如果之前的最大和最小值同当前元素相乘之后,没有当前元素大(或小)那么当前元素就可作为新的起点。例如,前一个元素为0的情况,{1,0,9,2},到9的时候9应该作为一个最大值,也就是新的起点,{1,0,-9,-2}也是同样道理,-9比当前最小值还小,所以更新为当前最小值。

TLE代码

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        if (nums.empty())
            return 0;

        int size = nums.size();
        //求从0~size-1处开始,每处能够得到的最大子数组乘积
        vector<int> maxP(size, 1);
        for (int i = 0; i < size; ++i)
        {
            maxP[i] = nums[i];
        }//for

        for (int i = 0; i < size; ++i)
        {
            int curP = maxP[i];

            for (int j = i + 1; j<size; ++j)
            {
                curP *= nums[j];
                if (maxP[i] < curP)
                    maxP[i] = curP;
            }           
        }//for

        //找到最大子数组乘积中的最大乘积值
        int maxRet = maxP[0];
        for (int i = 1; i < size; ++i)
        {
            if (maxP[i] > maxRet)
                maxRet = maxP[i];
        }//for
        return maxRet;
    }
};

AC代码

class Solution {
public:
    //方法一:遗憾的TLE
    int maxProduct1(vector<int>& nums) {
        if (nums.empty())
            return 0;

        int size = nums.size();
        //求从0~size-1处开始,每处能够得到的最大子数组乘积
        vector<int> maxP(size, 1);
        for (int i = 0; i < size; ++i)
        {
            maxP[i] = nums[i];
        }//for

        for (int i = 0; i < size; ++i)
        {
            int curP = maxP[i];

            for (int j = i + 1; j<size; ++j)
            {
                curP *= nums[j];
                if (maxP[i] < curP)
                    maxP[i] = curP;
            }           
        }//for

        //找到最大子数组乘积中的最大乘积值
        int maxRet = maxP[0];
        for (int i = 1; i < size; ++i)
        {
            if (maxP[i] > maxRet)
                maxRet = maxP[i];
        }//for
        return maxRet;
    }

    //方法二:时间复杂度为O(n)
    int maxProduct(vector<int>& nums) {
        if (nums.empty())
            return 0;

        int size = nums.size();

        //存储最大子数组乘积,当前最大、最小值
        int maxRetP = nums[0], curMaxP = nums[0], curMinP = nums[0];
        for (int i = 1; i < size; ++i)
        {
            int tmpMax = curMaxP * nums[i];
            int tmpMin = curMinP * nums[i];

            //更新当前最大、最小值
            curMaxP = max(max(tmpMax , tmpMin), nums[i]);
            curMinP = min(min(tmpMax , tmpMin), nums[i]);

            //更新当前最大子数组结果
            maxRetP = max(maxRetP, curMaxP);
        }//for
        return maxRetP;
    }
};

GitHub测试程序源码

posted on 2015-11-01 14:00  Coding菌  阅读(171)  评论(0编辑  收藏  举报