题目描述:
给出n个数字,代表直方图的条高,直方图每一条的宽度为1,请计算直方图中最大矩形的面积
上图是每条宽度为1, 高度 =[2,1,5,6,2,3].的直方图
图中的阴影部分是该直方图中面积最大的矩形,面积为10个单位
例如:
给出的高度 =[2,1,5,6,2,3],
返回10.
题目分析:
该题目在左神的算法课程中提到过使用单调栈的结构或思想,也就是使用栈或者一种类似于栈的先入后出的存储方式来解题目。构造一个从小到大的栈,只要遇到比当前栈顶元素大的值将其放入栈中(本题采用将元素的下标放入栈中,但概念是单调栈),如果遇到比栈顶小的元素,将其进行结算。结算的规则如下:
如果当前的栈顶元素对前面的元素没有归结则直接结算;
如果当前栈顶元素归结过前面的元素,则归结时将其归结记录加入
当前数组全部进入栈,剩余栈中的元素需要null对其进行归结,归结归入如上。
为什么要让小的元素归结大的元素以及记录前面元素的归结记录?
个人理解是:当前元素向右移动遇到比自己小的元素就不能进行移动了,因此需要小的元素将该元素归结;记录前面归结的记录是当前元素向左移动,当前元素归结的元素一定比当前元素大,可以左移,被其所归结的元素更大 ,因此也许进行记录。
代码如下:
import java.util.Stack;
public class Solution {
public int largestRectangleArea(int[] height) {
//边界判断,空数组直接返回0
if(height.length==0) return 0;
//定义栈,用来存放数组下标
Stack<Integer> stack=new Stack<>();
//定义额外的数组,记录当前的元素对于前面的元素是否归结,初始全0
int[] arr=new int[height.length];
stack.push(0);
//记录最大面积的值,初始值最小
int res=Integer.MIN_VALUE;
int count=0;//记录当前元素归结元素的个数
for(int i=1;i<height.length;i++) {
int sum=0;//记录归结的元素归结的元素个数
//遇到后面元素不能归结或者0元素,将归结记录置为0
if(!stack.isEmpty() &&height[stack.peek()]<=height[i]||height[i]==0) count=0;
//开始归结比当前元素大的栈中元素
while(!stack.isEmpty() && height[stack.peek()]>height[i]){
int tmp=stack.pop();
res=height[tmp]*(i+arr[tmp]-tmp)>res?height[tmp]*(i+arr[tmp]-tmp):res;
count++;
sum+=arr[tmp];
}
//记录当前元素的归结个数=归结的元素个数+归结元素所归结的个数
arr[i]=count+sum;
count=0;
stack.push(i);
}
//归结栈中剩余的元素
while(!stack.isEmpty()) {
/* if(stack.size()==1) {
int tmp=stack.pop();
res= height.length*height[tmp]>res?height.length*height[tmp]:res;
break;
}*/
int tmp=stack.pop();
res=height[tmp]*(height.length+arr[tmp]-tmp)>res?height[tmp]*(height.length+arr[tmp]-tmp):res;
}
return res;
}
}