[经典面试题]子数组的最大乘积

题目

给定一个长度为N的整数数组,只允许用乘法,不能用除法,计算任意(N-1)个数的组合乘积中的最大的一组,并写出算法的时间复杂度。

思路一 穷举法

我们把所有可能的(N-1)个数的组合找出来,分别计算它们的乘积,并比较大小。由于总共有N个(N-1)个数的组合,总的时间复杂度为O(N^2),但显然这不是最好的思路。

思路二 空间换时间

计算(N-1)个数的组合乘积,假设第i个(0<=i<=N-1)元素被排除在乘积之外(如下图)。

设num[]为初试数组

Left[i]表示前i个元素(包括第i个元素)的乘积(0<=i<=N-1)

Left[i] = Left[i-1] * num[i]

Right[i]表示后N-i个元素(包括第i个元素)的乘积(0<=i<=N-1)

Right[i] = Right[i+1] * num[i]

设p[i]为数组除第i个元素外,其他N-1个元素的乘积:

P[i] = Left[i-1] * Right[i+1]

举个例子:

P[4] = Left[3] * Right[5]

= a1 * a2 * a3 * a4 * a6 * a7 * a8 * a9 * a10

由于只需要从头到尾,从尾到头扫描两次即可得到数组Left[] 和 Right[],从而线性时间得到P[i]。所以很容易的就可以得到最大值(只需遍历一次p数组)。总的时间复杂度为计算数组Left[],Right[],p[]的时间复杂度加上查找p[]最大值的时间复杂度即O(N)。

代码一

  /*---------------------------------------------
    *   日期:2015-02-15
    *   作者:SJF0115
    *   题目: 子数组的最大乘积
    *   来源:
    *   博客:
    -----------------------------------------------*/
    #include <iostream>
    #include <cstring>
    using namespace std;

    class Solution {
    public:
        double MaxProduct(double num[],int n){
            if(n <= 0){
                return 0;
            }//if
            double max = num[0],p;
            double Left[n],Right[n];
            // 初始化
            Left[0] = num[0];
            Right[n-1] = num[n-1];
            // 计算Left数组
            for(int i = 1;i < n;++i){
                Left[i] = Left[i-1] * num[i];
            }//for
            // 计算Right数组
            for(int i = n-2;i >= 0;--i){
                Right[i] = Right[i+1] * num[i];
            }//for
            // max
            int left,right;
            for(int i = 0;i < n;++i){
                left = (i - 1) >= 0 ? Left[i-1] : 1;
                right = (i + 1) < n ? Right[i+1] : 1;
                p = left * right;
                if(p > max){
                    max = p;
                }//if
            }//for
            return max;
        }
    };

    int main() {
        Solution solution;
        double num[] = {-2.5,-4,0.5,3,1,2,-3};
        cout<<solution.MaxProduct(num,7)<<endl;
    }

思路二

通过分析,进一步减少计算量。假设N个数的乘积为P,针对P的正负性进行如下分析:

(1)P为0

那么,数组中至少包含一个0。假设除去一个0之外,其他N-1个数的乘积为Q,根据Q的正负性进行讨论:

Q为0,说明数组中至少有两个0,那么N-1个数的乘积只能为0。

Q为正,返回Q。因为如果用0替换剩余N-1个数中的任意一个,所得乘积结果都为0,小于之前的Q,因此乘积最大值为Q。

Q为负,返回0。因为如果用0替换剩余N-1个数中的任意一个,所得结乘积果都为0,大于之前的Q,因此乘积最大值为0。

(2)P为负

根据“负负得正”的乘法性质,自然想到的是从N个整数中去掉一个负数,使得N-1个数乘积为正数。而要使这个整数最大,这个被去掉的负数绝对值必须是数组中最小的。我们只需要扫描一遍数组,把绝对值最小的负数去掉就可以了。

(3)P为正

类似P为负的情况,应该去掉一个绝对值最小的正数值,这样得到的N-1个数乘积就是最大的。

上面的思路采用了直接求N个数的乘积P,进而判断P的正负性的办法,但是直接求乘积往往有溢出的危险,事实上可做一个小的转变,不需要直接乘积,而是求出数组中正数,负数和0的个数,从而判断P的正负性。

在时间复杂度上,由于只需要一次遍历数组,在遍历数组的同时就可得到数组中正数,负数和0的个数,以及数组中绝对值最小的正数和负数,时间复杂度为O(N)

代码二

    /*---------------------------------------------
    *   日期:2015-02-15
    *   作者:SJF0115
    *   题目: 子数组的最大乘积
    *   来源:
    *   博客:
    -----------------------------------------------*/
    #include <iostream>
    #include <climits>
    #include <cmath>
    using namespace std;

    class Solution {
    public:
        double MaxProduct(double num[],int n){
            if(n <= 0){
                return 0;
            }//if
            // 绝对值最小的负数,绝对值最小的正数
            double pMin = INT_MAX,nMin = INT_MAX;
            // 绝对值最小的负数,绝对值最小的正数,0的下标
            int pIndex = 0,nIndex = 0,zeroIndex;
            // 0,正数,负数个数
            int zCount = 0,pCount = 0,nCount = 0;
            // 去除掉元素的下标
            int index = 0;
            // 统计
            for(int i = 0;i < n;++i){
                if(num[i] == 0){
                    zeroIndex = i;
                    ++zCount;
                }//if
                else if(num[i] > 0){
                    ++pCount;
                    // 绝对值最小的正数
                    if(num[i] < pMin){
                        pIndex = i;
                        pMin = num[i];
                    }//if
                }//else
                else{
                    ++nCount;
                    // 绝对值最小的负数
                    if(fabs(num[i]) < nMin){
                        nMin = fabs(num[i]);
                        nIndex = i;
                    }//if
                }//else
            }//for
            // P为0
            if(zCount > 0){
                // Q为0
                if(zCount - 1 > 0){
                    return 0;
                }//if
                // Q为负
                if(nCount % 2){
                    return 0;
                }//if
                // Q为正
                else{
                    // 去掉下标为zeroIndex的元素
                    index = zeroIndex;
                }//else
            }//if
            // P为正去掉绝对值最小的正数
            else if(nCount % 2 == 0){
                index = pIndex;
            }
            // P为负去掉绝对值最小的负数
            else{
                index = nIndex;
            }//else
            // 最大乘积
            double max = 1;
            for(int i = 0;i < n;++i){
                if(i != index){
                    max *= num[i];
                }//if
            }//for
            return max;
        }
    };

    int main() {
        Solution solution;
        double num[] = {2.5,4,-0.5,3,-1,2,3};
        cout<<solution.MaxProduct(num,7)<<endl;
    }

时间: 2024-10-12 13:51:50

[经典面试题]子数组的最大乘积的相关文章

经典算法——连续子数组的最大乘积

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.

【编程之美】2.13 子数组的最大乘积

题目:一个有N个数的整数数组 取其中N-1个元素的子数组 求子数组的最大乘积 不能用除法. 这道题自己没有写对,没有考虑到负数的情况,只是单纯的想去掉最小的数. 但是若有负数 -5 -4 -3 中-5 * -4 = 20更大. 需要先统计正数.负数和0的个数,再分类讨论.考察的其实就是细心和耐心. //答案解法 int getMaxProductAnswer(int * a, int alen) { int NumPositive = 0; int NumNegtive = 0; int Num

子数组的最大乘积

这个题目的意思是在一个含有N个数字的数组中,找出N-1个数字,使得这N-1个数字的乘积最大,不允许使用除法. 一开始看这个题的感觉可能是很简单,我只要找出这个数中最小的值,那么剩余的N-1个数的乘积一定是最大的. 但是这就忽略了一个情况,就是存在负数的情况.题目中并没有说是个正数的数组.因此在拿到一道题目时,一定要尽可能的想清楚题目的各个方面. 但是我们依然可以利用上面的这个思路进行查找.首先遍历一遍数组,记录负值绝对值最大和最小的点,0的个数,正数的个数和负数的个数.完成遍历之后,就可以根据以

编程之美2.13 子数组的最大乘积

      这道题目是求 n-1 个数的最大乘积,即数组大小为 n,则会存在 n 个 n-1 的连续数字,那么,我们需要寻找的是最大的那一个乘积.       其实看到题目,感觉很简单,循环走两遍数组就可以得到结果,但是,那样的话,复杂度是平方量级的,如果一个数组中元素很多,那么,时间效率上是不能接受的,所以,需要重新思考问题,寻找简洁的解法.       这里,我们可以利用辅助数组的办法,把需要乘在一起的数字保存下来,单独的放到数组中,然后乘在一块就可以了,这样说起来其实不是那么容易理解,我还

2.13 子数组的最大乘积

题目: 给定一个长度为N的整形数组,只允许用乘法,不能用除法.计算任意N-1个数的组合中乘积最大的一组. 方法一: #include <iostream> #define MAXN 10000 using namespace std; int n, a[MAXN], s[MAXN], t[MAXN], p[MAXN]; //s[i]表示数组前i个元素的乘积 //t[i]表示数组后N-i个元素的乘积 //p[i] = s[i-1] * t[i+1]; //p[i]表示数组除第I个元素外,其他N-

【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 = 

在一个数组中找到连续的子数组最大的乘积

原题目:https://oj.leetcode.com/problems/maximum-product-subarray/ 例如输入[2,3,-2,4]? 符合条件的子数组应该是[2,3],他们的乘积是6 /** * @Author jiangfq * */ package com.test; /** * @author jiangfq * */ public class Solution { /** * @Author jiangfq * */ public static void main

经典算法——连续子数组最大和问题

Find the contiguous subarray within an array (containing at least one number) which has the largest sum. For example, given the array [?2,1,?3,4,?1,2,1,?5,4], the contiguous subarray [4,?1,2,1] has the largest sum = 6. 一.问题描述:输入一个整数数组,求数组中连续的子数组使其和最大

【编程之美】子数组的最大乘积

给定一个长度为N的整数数组,只允许用乘法,不能用除法,计算任意(N-1)个数的组合中乘积最大的一组,并写出算法的时间复杂度. 我们把所有可能的(N-1)个数的组合找出来,分别计算它们的乘积,并比较大小.由于总共有N个(N-1)个数的组合,总的时间复杂度为O(N2),显然这不是最好的解法. 分析与解法 解法一:空间换时间 用s[i]表示数组的前i个元素的乘积,即s[i]=a[0]*a[1]*...*a[i-1]=s[i-1]*a[i-1],边界s[0]=1: 用t[i]表示数组的后N-i个元素的乘