题目
https://leetcode-cn.com/problems/largest-rectangle-in-histogram/
二刷
参考:https://www.sohu.com/a/454782608_120437685
构建一个单调递增的单调栈。求以栈顶高度为高的矩形宽度,就要找到左边和右边第一个小于它的元素。左边界(开区间)是前一个栈中元素,右边界(开区间)是右边第一个小于它的元素。
class Solution {
public int largestRectangleArea(int[] heights) {
int[] height=new int[heights.length+2];
height[0]=0;
height[heights.length+1]=0;//数组首尾添加0
for(int i=0;i<heights.length;i++){
height[i+1]=heights[i];
}
LinkedList<Integer> stack=new LinkedList<>();
stack.addLast(0);
int max=0;
for(int i=1;i<height.length;i++){
while(!stack.isEmpty()&&height[stack.peekLast()]>=height[i]){
int index=stack.removeLast();
int h=height[index];//要计算这个高度能勾勒的矩形面积
int len=i-index;//宽度
if(!stack.isEmpty()){
len=i-stack.peekLast()-1;
}
max=Math.max(max,h*len);
}
stack.addLast(i);
}
return max;
}
}
方法一:单调栈
分析
暴力解法:矩形面积=底*高。底不好固定,所以按高度固定。对于不同的高度,向左右扩散直到有小于它的高度,就可以知道这个高度能勾勒的最大矩形面积。时间复杂度是O(n²)。
优化的思路是以空间换时间。
特殊情况
1.遍历完成后,栈顶元素一定可以扩散到数组末尾
2.弹出栈顶以后栈为空,当前数一定可以扩散到数组的最左边
3.栈中存在连续的高度相等的元素,新栈顶和当前栈顶一样时,做特殊的判断。不过不做也行,虽然计算不准确,但不影响最终结果。
复杂度
时间复杂度:O(N)。
空间复杂度:O(N)。
代码
class Solution {
public int largestRectangleArea(int[] heights) {
int len=heights.length;
if(len==0) return 0;
if(len==1) return heights[0];
Deque<Integer> stack=new LinkedList<>();
int area=0;
for(int i=0;i<len;i++){
while(!stack.isEmpty()&&heights[i]<heights[stack.peekLast()]){
int height=heights[stack.removeLast()];
while(!stack.isEmpty()&&height==heights[stack.peekLast()]){
stack.removeLast();
}
int width;
if(stack.isEmpty()){
width=i;
}
else{
width=i-stack.peekLast()-1;
}
area=Math.max(area,width*height);
}
stack.addLast(i);
}
while(!stack.isEmpty()){
int height=heights[stack.removeLast()];
while(!stack.isEmpty()&&height==heights[stack.peekLast()]){
stack.removeLast();
}
int width;
if(stack.isEmpty()){
width=len;
}
else{
width=len-stack.peekLast()-1;
}
area=Math.max(area,width*height);
}
return area;
}
}
结果
方法一的优化:哨兵
哨兵优化
class Solution {
public int largestRectangleArea(int[] heights) {
int len=heights.length;
if(len==0) return 0;
if(len==1) return heights[0];
Deque<Integer> stack=new LinkedList<>();
int area=0;
int[] he=new int[len+2];
he[0]=0;
he[len-1]=0;
for(int i=0;i<len;i++){
he[i+1]=heights[i];
}
stack.addLast(0);
for(int i=1;i<len+2;i++){
while(he[i]<he[stack.peekLast()]){
int height=he[stack.removeLast()];
int width=i-stack.peekLast()-1;//这很关键!!!
area=Math.max(area,width*height);
}
stack.addLast(i);
}
return area;
}
}
class Solution {
public int largestRectangleArea(int[] heights) {
int len=heights.length;
int max=heights[0];
int[] h=new int[len+2];
h[0]=0;
h[len+1]=0;
for(int i=1;i<=len;i++){
h[i]=heights[i-1];
}
LinkedList<Integer> stack=new LinkedList<>();
//单调增栈,存放index
for(int i=0;i<len+2;i++){
if(stack.isEmpty()||h[stack.peek()]<=h[i]){
stack.push(i);
}
else{
while(!stack.isEmpty()&&h[stack.peek()]>h[i]){
int index=stack.pop();//他找到了自身高度的最大面积
int left=index;
if(!stack.isEmpty()){
left=stack.peek()+1;
}
int right=i-1;//i的高度小于
max=Math.max(max,h[index]*(right-left+1));
}
stack.push(i);
}
}
return max;
}
}