携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第32天,点击查看活动详情
题目描述
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
示例 1:
输入:heights = [2,1,5,6,2,3] 输出:10 解释:最大的矩形为图中红色区域,面积为 10 示例 2:
输入: heights = [2,4] 输出: 4
提示:
- 1 <= heights.length <=105
- 0 <= heights[i] <= 104
解题思路
给定一个数组heights,模拟一副柱状图,数组中每个元素对应每个坐标的柱状高度,每根柱子宽度为1;求柱状图中能形成的最大矩形的面积;
这道题和求能接到的最大雨水那道题一点相似。但是区别就在这里求的是能形成的矩形,而那道题求得是最大重叠面积,即多个矩形重叠在一起也是可以的。
首先确定柱状图中每根柱子能形成的最大矩形都是怎么获取到的?
假定heights=[2,3,4,1,2];
那么第一根柱子能形成的最大矩形取决于从它的位置向后查找的第一个比它小的值的位置,因为要形成一个矩形高肯定是相等的,而当前元素能形成的最大矩形的高度就是它自己(再比它高就成不了矩形了);
也就是说第一根柱子2能形成的最大矩形为2*3=6;
继续查找第二根柱子3,它向后查找,第一个小于它的柱子位于3;但是这里它还有前面的元素,所以除了第一根柱子以外,剩下的柱子都要向前面寻找第一根小于它的柱子。所以第二根柱子的最大矩形面积是3*(1+1)=6;
依次类推,直到查找到最后一根柱子,此时最后一根柱子因为没有后面的柱子,所以只需要向前寻找第一个小于它的柱子,没有的话就是它本身,即最后一根柱子能形成的最大矩形面积就是2;
由此可见,在数组heights中,要寻找最大矩形面积,都是当前元素向前或者向右寻找第一个小于它的元素,然后求和即可。所以这里我们可以套用哨兵节点的思想,即创建一个不存在的节点来使所有节点都可以用同样的操作,降低代码复杂度;
像这种需要按照方向求第一个大的或者小的值,可以用单调栈来求解。
代码实现
public static int largestRectangleArea(int[] heights) {
int length = heights.length;
int[] left = new int[heights.length];
int[] right = new int[heights.length];
// 向前寻找第一个小于它的柱子
ArrayDeque<Integer> arrayDeque = new ArrayDeque<>();
for (int i=0; i< length; i++) {
// 单调栈中只要有值就可以一直弹出
while (!arrayDeque.isEmpty() && heights[arrayDeque.peek()] >= heights[i]) {
arrayDeque.poll();
}
// 此时单调栈中的元素一定是第一个小于当前元素的值,如果为空,则直接将这个位置赋值为-1
left[i] = arrayDeque.isEmpty() ? -1 : arrayDeque.peek();
arrayDeque.push(i);
}
// 清空单调栈
arrayDeque.clear();
for (int i=length-1; i>= 0; i--) {
// 单调栈中只要有值就可以一直弹出
while (!arrayDeque.isEmpty() && heights[arrayDeque.peek()] >= heights[i]) {
arrayDeque.poll();
}
// 此时单调栈中的元素一定是第一个小于当前元素的值,如果为空,则直接将这个位置赋值为n
right[i] = arrayDeque.isEmpty() ? length : arrayDeque.peek();
arrayDeque.push(i);
}
// 获取最大面积
int max = 0;
for (int i=0 ; i < length ; i ++) {
max = Math.max(max,heights[i] * (right[i]-left[i]-1));
}
return max;
}