问题描述
小S最近在分析一个数组 ,数组的每个元素代表某种高度。小S对这些高度感兴趣的是,当我们选取任意 个相邻元素时,如何计算它们所能形成的最大矩形面积。
对于 个相邻的元素,我们定义其矩形的最大面积为:
即, 的值为这 个相邻元素中的最小值乘以 。现在,小S希望你能帮他找出对于任意 , 的最大值。
测试样例
样例1:
输入:
n = 5, array = [1, 2, 3, 4, 5]输出:9
样例2:
输入:
n = 6, array = [5, 4, 3, 2, 1, 6]输出:9
样例3:
输入:
n = 4, array = [4, 4, 4, 4]输出:16
思路
对于数组 array :我们可以将其中的每个元素想象成一个高度为 array[i] 、宽为 1 的矩形,在这些连续矩形的范围中找到一个可以形成的最大面积的矩形。
对于最大面积:我们可以遍历数组中的每一个元素,统计以 array[i] 为目标矩形高度、且包含当前位置的矩形的最大面积。
因此,在已知高度的情况下,我们只需要知道每一个位置的最大宽度是多少,即可求出一个位置的最大矩形面积,最后对这些面积取最值即可得到答案。
如何求最大宽度
当高度已知时,我们可以用分解前后缀的思想,向两边扩展,当遇到数组边界或者第一个高度小于当前高度的位置,就结束。
扩展时如何判断边界条件
典型的下一个更大/小元素问题,可以使用单调栈解决。简单来说就是维护一个单调的栈,栈中元素自顶向底具有单调性。
在本题中,我们需要寻找下一个更小元素,因此需要维护一个自顶向下递减栈,对于每个位置,我们检查栈顶元素是否小于当前位置,如是,则直接记录位置,否则出栈,继续检查,直到找到更小的元素或者栈为空(为空时范围设置为数组边界)。
以下展示寻找元素 右侧 的下一个更小元素的Java代码:
// 双端队列模拟栈。元素[值, 位置]
// 约定First为底,Last为顶
Deque<int[]> stack = new LinkedList<>();
for (int i = arr.length - 1; i >= 0; i--) {
// 当前位置与栈顶元素比较,栈不为空且栈顶元素大于当前元素,出栈
while (!stack.isEmpty() && stack.getLast()[0] > arr[i]) stack.pollLast();
// 栈空
if (stack.isEmpty()) nex[i] = arr.length;
// 非空
else {
// 特殊情况,见tips
if (arr[stack.getLast()[1]] == arr[i]) nex[i] = nex[stack.getLast()[1]];
// 正常情况
else nex[i] = stack.getLast()[1];
}
// 当前位置入栈
stack.addLast(new int[]{arr[i], i});
}
特殊情况
对于用例3:n = 4, array = [4, 4, 4, 4],由于只有栈顶元素大于当前位置时才会出栈,因此对于值相同的情况,如果不做特殊处理,对于任意一个位置,都会出现下一个更小元素为相邻元素的情况。比如对于第2个4:右侧的下一个更小元素为第3个4,而不是数组的右边界。
因此当元素相同时,代码应为:
// 特殊情况
if (arr[stack.getLast()[1]] == arr[i]) nex[i] = nex[stack.getLast()[1]];
// 正常情况
else nex[i] = stack.getLast()[1];
代码
int[] nex = new int[arr.length], pre = new int[arr.length];
// 高度、位置
// 单调栈
Deque<int[]> stack = new LinkedList<>();
// 记录右
for (int i = arr.length - 1; i >= 0; i--) {
while (!stack.isEmpty() && stack.getLast()[0] > arr[i]) stack.pollLast();
if (stack.isEmpty()) nex[i] = arr.length;
else {
if (arr[stack.getLast()[1]] == arr[i]) nex[i] = nex[stack.getLast()[1]];
else nex[i] = stack.getLast()[1];
}
// 当前位置入栈
stack.addLast(new int[]{arr[i], i});
}
stack.clear();
// 记录左
for (int i = 0; i < arr.length; i++) {
while (!stack.isEmpty() && stack.getLast()[0] > arr[i]) stack.pollLast();
if (stack.isEmpty()) pre[i] = -1;
else {
if (arr[stack.getLast()[1]] == arr[i]) pre[i] = pre[stack.getLast()[1]];
else pre[i] = stack.getLast()[1];
}
// 当前位置入栈
stack.addLast(new int[]{arr[i], i});
}
// 计算结果
int res = 0;
for (int i = 0; i < arr.length; i++) {
res = Math.max(res, (nex[i] - pre[i] - 1) * arr[i]);
}
return res;
复杂度
时间复杂度: O(n),三次遍历。
空间复杂度: O(n),两个一维数组,长度为N,一个Stack,最多存储N个元素。