LeetCode84. 柱状图中最大的矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。
图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。
示例:
输入: [2,1,5,6,2,3]
输出: 10
解题思路:
这个题目在leetcode上面标注的是hard级别,也就是说题目比较难。倒不是说题目晦涩难懂,找不到根本的解法突破口,而是说如何利用相应的算法将复杂度降低。这个题目一眼就可以看出来采用暴力解法是行的通但是通不过的。所以我们需要另辟蹊径。
解法一 分而治之
通过观察,可以发现,最大面积矩形存在于以下几种情况:
确定了最矮柱子以后,矩形的宽尽可能往两边延伸。
在最矮柱子左边的最大面积矩形(子问题)。
在最矮柱子右边的最大面积矩形(子问题)。
举个例子:
[6, 4, 5, 2, 4, 3, 9]
这里最矮柱子高度为 2 。以 2 为高的最大子矩阵面积是 2x7=14 。现在,我们考虑上面提到的第二种和第三种情况。我们对高度为 2 柱子的左边和右边采用同样的过程。在 2 的左边, 4 是最小的,形成区域为 4x3=12 。将左边区域再继续分,矩形的面积分别为 6x1=6 和 5x1=5 。同样的,我们可以求出右边区域的面积为 3x3=9, 4x1=4 和 9x1=9 。因此,我们得到最大面积是 16 。具体过程可参考下图:
class Solution {
public int largestRectangleArea(int[] heights) {
return calculateArea(heights, 0, heights.length - 1);
}
public int calculateArea(int[] heights, int start, int end){
if(start>end){
return 0;
}
int minIndex=start;
for(int i=start;i<=end;i++){
if(heights[i]<heights[minIndex]){
minIndex=i;
}
}
return Math.max(heights[minIndex] * (end - start + 1), Math.max(calculateArea(heights, start, minIndex - 1), calculateArea(heights, minIndex + 1, end)));
}
}
解法二 单调栈
class Solution {
public int largestRectangleArea(int[] heights) {
//写一个比较好理解的代码思路。首先需要明确一点就是在遍历的过程如果出现下降的位置那么最大的面积就只能是在其前面
if(heights==null || heights.length==0) return 0;
int maxArea=0;
Stack<Integer> s=new Stack<>();
for(int i=0;i<heights.length;i++){
while(!s.isEmpty() && heights[i]<=heights[s.peek()]){
int j=s.pop(); //这里已经是出栈了,即栈顶的元素已经改变了
int k=s.isEmpty()? -1:s.peek();
maxArea=Math.max(maxArea,heights[j]*(i-k-1));
}
s.push(i);
}
//处理边界情况,所给的序列一直递增的情况下,最后栈不为空且heights已经遍历完成了
while(!s.isEmpty()){
int j=s.pop();
int k=s.isEmpty()? -1:s.peek();
maxArea=Math.max(maxArea,heights[j]*(heights.length-k-1));
}
return maxArea;
}
}