您的位置:首页 > 职场人生

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

2013-10-10 23:51 489 查看
连续子数组求和最大值比较常见,乘积与求和有相通之处。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是最大的,而且是连续的。

解法一:

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

double MaxMul(double *num, int len)
{
if(num == NULL || len < 1)
return 0;

double *maxMul = new double[len];
double *minMul = new double[len];
maxMul[0] = num[0];
minMul[0] = num[0];
double maxM = num[0];
for(int i = 1; i < len; ++i)
{
maxMul[i] = maxMul[i-1]*num[i] > num[i] ? maxMul[i-1]*num[i] : num[i];
maxMul[i] = maxMul[i] > minMul[i-1]*num[i] ? maxMul[i] : minMul[i-1]*num[i];
minMul[i] = minMul[i-1]*num[i] < num[i] ? minMul[i-1]*num[i] : num[i];
minMul[i] = minMul[i] < maxMul[i-1]*num[i] ? minMul[i] : maxMul[i-1]*num[i];
maxM = maxM > maxMul[i] ? maxM : maxMul[i];
}
delete[] maxMul;
delete[] minMul;
return maxM;
}


解法二:

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

double MaxMul2(double *num, int len)
{
if(num == NULL || len < 1)
return 0;

double maxM = num[0];
double maxMul = num[0];
double minMul = num[0];
double maxCur = num[0];
double minCur = num[0];
for(int i = 1; i < len; ++i)
{
if(maxCur < 1)
maxCur = num[i];
else
maxCur *= num[i];
minCur *= num[i];
if(maxCur < minCur)
{
double tmp = maxCur;
maxCur = minCur;
minCur = tmp;
}
maxMul = maxMul > maxCur ? maxMul : maxCur;
minMul = minMul < minCur ? minMul : minCur;
}
return maxMul;
}


两个解法的时间复杂度都是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

代码如下:

int MaxArea(int *num, int len)
{
if(num == NULL || len < 1)
return 0;

stack<int> numStack;
stack<int> indStack;
numStack.push(num[0]);
indStack.push(0);
int lastPopInd = 0;
int maxMul = num[0];
for(int i = 1; i < len; ++i)
{
if(num[i] > numStack.top())
{
numStack.push(num[i]);
indStack.push(i);
}
else if(num[i] < numStack.top())
{
while(!numStack.empty() && num[i] < numStack.top())
{
int numPop = numStack.top();
lastPopInd = indStack.top();
maxMul = (numPop * (i - lastPopInd)) > maxMul ? (numPop * (i - lastPopInd)) : maxMul;
numStack.pop();
indStack.pop();
}
if(numStack.empty() || num[i] > numStack.top())
{
numStack.push(num[i]);
indStack.push(lastPopInd);
}
}
}
while(!numStack.empty())
{
int numPop = numStack.top();
lastPopInd = indStack.top();
maxMul = (numPop * (len - lastPopInd)) > maxMul ? (numPop * (len - lastPopInd)) : maxMul;
numStack.pop();
indStack.pop();
}
return maxMul;
}


扩展问题是:

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

可以生成一个新的矩阵C,C[i][j]表示第j列,从第i个元素开始,包括第i个元素,向上数,直到遇到0时,1的个数。然后再每一行按一维做就可以了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: