zoukankan      html  css  js  c++  java
  • 【面试题】连续子数组乘积最大值与柱状图中找最大矩形

    连续子数组求和最大值比较常见,乘积与求和有相通之处。10月9号美团的笔试考到了这题。

    第一题:原题

    给一个浮点数序列,求连续子串乘积的最大值,例如 -2.5,4,0,3,0.5,8,-1,则取出的最大乘积连续子串为3,0.5,8。也就是说,上述数组中,3 0.5 8这3个数的乘积3*0.5*8=12是最大的,而且是连续的。

    解法一:

    用动态规划很好做。问题的关键是,序列中有正数也有负数,所以,需要记录最大值,也要记录最小值。

     1 double MaxMul(double *num, int len)
     2 {
     3     if(num == NULL || len < 1)
     4         return 0;
     5 
     6     double *maxMul = new double[len];
     7     double *minMul = new double[len];
     8     maxMul[0] = num[0];
     9     minMul[0] = num[0];
    10     double maxM = num[0];
    11     for(int i = 1; i < len; ++i)
    12     {
    13         maxMul[i] = maxMul[i-1]*num[i] > num[i] ? maxMul[i-1]*num[i] : num[i];
    14         maxMul[i] = maxMul[i] > minMul[i-1]*num[i] ? maxMul[i] : minMul[i-1]*num[i];
    15         minMul[i] = minMul[i-1]*num[i] < num[i] ? minMul[i-1]*num[i] : num[i];
    16         minMul[i] = minMul[i] < maxMul[i-1]*num[i] ? minMul[i] : maxMul[i-1]*num[i];
    17         maxM = maxM > maxMul[i] ? maxM : maxMul[i];
    18     }
    19     delete[] maxMul;
    20     delete[] minMul;
    21     return maxM;
    22 }

    解法二:

    也可以扫一遍序列做出来,不过也要记录最大值和最小值,以及当前的最大值和最小值,当当前的最大值小于1的时候,设置为当前的序列值,呃,感觉语言有点绕,+_+,可以看《剑指offer》面试题31:连续子数组的最大和,类比一下就行了。还是用代码说话吧,逻辑会比较清楚。

     1 double MaxMul2(double *num, int len)
     2 {
     3     if(num == NULL || len < 1)
     4         return 0;
     5 
     6     double maxM = num[0];
     7     double maxMul = num[0];
     8     double minMul = num[0];
     9     double maxCur = num[0];
    10     double minCur = num[0];
    11     for(int i = 1; i < len; ++i)
    12     {
    13         if(maxCur < 1)
    14             maxCur = num[i];
    15         else
    16             maxCur *= num[i];
    17         minCur *= num[i];
    18         if(maxCur < minCur)
    19         {
    20             double tmp = maxCur;
    21             maxCur = minCur;
    22             minCur = tmp;
    23         }
    24         maxMul = maxMul > maxCur ? maxMul : maxCur;
    25         minMul = minMul < minCur ? minMul : minCur;
    26     }
    27     return maxMul;
    28 }

    两个解法的时间复杂度都是O(1),解法一的空间复杂度是O(n),解法二的空间复杂度也是O(1)。其实解法一的空间复杂度也可以降为O(1),不用数组,用四个变量即可。

    第二题:原题

    在柱状图中找最大的矩形:给一组非负的整数来表示一个柱状图,设计一个算法找到最大面积的能适合到柱状图内的矩形。比如,对于这组数,1 2 3 4 1 ,有两种可能的方案,一种是适合到 2 3 4 内的矩形,面积是 2*3;另一种是适合到 3 4 内的矩形,面积是 3*2。

    用数学点的描述就是,找所给数组的一个连续子数组,使该子数组的最小值与数组长度乘积最大。

    解法是在待字闺中微信公众账号里面看到的,一个线性算法是用堆栈来保存当前可能的矩形(高度和起始位置)。从左到右扫描,对一个元素,如果

    a)大于栈顶元素, push;

    b)小于的话,pop所有的大于它的元素,计算面积,更新最大值。这时如果堆栈空,push一个新的元素,高度等于当前元素,起始位置为0;否则,push当前元素高度和栈顶的起始位置。

    比如1 3 2 2 3这个数组,操作如下:

    i

    C[i]

    栈操作

    最大值

    栈内容

    0

    1

    push (1,0)

     

    (1,0)

    1

    3

    push (3,1)

     

    (3,1)(1,0)

    2

    2

    pop (3,1),push (2,1)

    (2-1)*3=3

    (2,1)(1,0)

    3

    2

    什么都不做

     

    (2,1)(1,0)

    4

    3

    push(3,4)

     

    (3,4)(2,1)(1,0)

    5

    0

    pop(3,4)

    (5-4)*3=3

    (2,1)(1,0)

    5

    0

    pop(2,1)

    (5-1)*2=8

    (1,0)

    5

    0

    pop(1,0)

    (5-0)*1=5

     

    代码如下:

     1 int MaxArea(int *num, int len)
     2 {
     3     if(num == NULL || len < 1)
     4         return 0;
     5 
     6     stack<int> numStack;
     7     stack<int> indStack;
     8     numStack.push(num[0]);
     9     indStack.push(0);
    10     int lastPopInd = 0;
    11     int maxMul = num[0];
    12     for(int i = 1; i < len; ++i)
    13     {
    14         if(num[i] > numStack.top())
    15         {
    16             numStack.push(num[i]);
    17             indStack.push(i);
    18         }
    19         else if(num[i] < numStack.top())
    20         {
    21             while(!numStack.empty() && num[i] < numStack.top())
    22             {
    23                 int numPop = numStack.top();
    24                 lastPopInd = indStack.top();
    25                 maxMul = (numPop * (i - lastPopInd)) > maxMul ? (numPop * (i - lastPopInd)) : maxMul;
    26                 numStack.pop();
    27                 indStack.pop();
    28             }
    29             if(numStack.empty() || num[i] > numStack.top())
    30             {
    31                 numStack.push(num[i]);
    32                 indStack.push(lastPopInd);
    33             }
    34         }
    35     }
    36     while(!numStack.empty())
    37     {
    38         int numPop = numStack.top();
    39         lastPopInd = indStack.top();
    40         maxMul = (numPop * (len - lastPopInd)) > maxMul ? (numPop * (len - lastPopInd)) : maxMul;
    41         numStack.pop();
    42         indStack.pop();
    43     }
    44     return maxMul;
    45 }

    扩展问题是:

    在一个位图中找面积最大的白色矩形:给你一个NxN的黑白位图,找一个面积最大的全白色的矩形。注意了,是一个矩形,不是任意一个白色相连的区域。

    可以生成一个新的矩阵C,C[i][j]表示第j列,从第i个元素开始,包括第i个元素,向上数,直到遇到0时,1的个数。然后再每一行按一维做就可以了。

  • 相关阅读:
    Java源码赏析(四)Java常见注解
    Java源码赏析(三)初识 String 类
    Java源码赏析(二)Java常见接口
    Java源码赏析(一)Object 类
    Java随谈(二)对空指针异常的碎碎念
    Java随谈(一)魔术数字、常量和枚举
    jquery.validate 使用--验证表单隐藏域
    jquery.validate使用
    jquery.validate使用
    jquery.validate使用
  • 原文地址:https://www.cnblogs.com/shirley130912/p/3362484.html
Copyright © 2011-2022 走看看