题目解析 最大矩形面积问题 | 豆包MarsCode AI刷题

91 阅读3分钟

问题描述

小S最近在分析一个数组 h1,h2,...,hNh_1, h_2, ..., h_N,数组的每个元素代表某种高度。小S对这些高度感兴趣的是,当我们选取任意 kk 个相邻元素时,如何计算它们所能形成的最大矩形面积。

对于 kk 个相邻的元素,我们定义其矩形的最大面积为:

R(k)=k×min(h[i],h[i+1],...,h[i+k1])R(k) = k \times min(h[i], h[i + 1], ..., h[i + k - 1])

即,R(k)R(k) 的值为这 kk 个相邻元素中的最小值乘以 kk。现在,小S希望你能帮他找出对于任意 kkR(k)R(k) 的最大值。


测试样例

样例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个元素。