【面试题】连续子数组乘积最大值与柱状图中找最大矩形

连续子数组求和最大值比较常见,乘积与求和有相通之处。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的个数。然后再每一行按一维做就可以了。

posted @ 2013-10-10 23:51  1加1equal雨山  阅读(1681)  评论(2编辑  收藏  举报