LargestRectangleInHistogram_84
题目描述
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。
图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。
示例:
输入: [2,1,5,6,2,3]
输出: 10
思路分析
解法1:暴力解法1
首先从处理1个矩形的情况开始,也就是它本身的大小,将这个值记作最大值,然后处理两个矩形的情况,这就有两种情况了,它本身的大小,它和第一个矩形的高度最小值*宽度,将他们与最大值比较,然后是第三个矩形,它就需要计算包括:它自己的大小,它和它前一个勾勒出来的矩形的大小,它和它前两个勾勒出来的矩形大小,然后与最大值进行比较,之后的一次类推。
解法2:暴力解法2
计算一个矩形的大小,我们需要确定的它的长和宽,宽是不好确定的,但是我们可以确定它的高,我们可以从每一个矩形出发,计算以它为高的矩形的最大面积,遍历完所有的矩形之后,最大的矩形面积就出来了
解法3:利用单调栈加哨兵
这个算法的核心思想就是,确定一个矩形以它为高能勾勒出来的最大面积,在遇到第一个比它矮的矩形的时候就已经定下来了,并且我们是先遍历的后确定,满足栈这种数据结构的特点,另外,为了在两个端点方便处理,构建了一个新的数组,在数组前后各补充两个0,例如:原数组={2,1,5,6,2,3},更新后的数组={0,2,1,5,6,2,3,0}。并且在一开始就将0入栈
好了,我们可以开始遍历了,从新数组的第1位开始,如果遇到元素它比栈顶的元素大,那么暂时它所能勾勒出来的最大矩形我们是没法确定的,这时候将它的位置入栈,并不是它对应的属性值,它的属性值可以直接通过数组访问,当遇到一个比栈顶元素高度小的矩阵时,这个时候栈顶这个元素所能勾勒出来的最大矩形我们就能确定了,它的高就是栈定元素,宽是当前序号-栈定-1,如果这个时候栈顶还满足大于当前元素,那么则继续弹栈,计算方式与上面相同。
为什么宽度可以这样确定呢?
因为我们的栈是单调的,当前栈顶元素的高度肯定是比之前栈定元素小的,所以肯定可以往那么去勾勒。
Java代码
暴力算法1
//暴力算法
public static int largestRectangle(int[] heights){
if (heights.length==0)
return 0;
int max=heights[0];
for (int i = 1; i < heights.length; i++) {
int this_max=heights[i];
int this_min=heights[i];
for (int j=i-1;j>=0;j--){
this_min=Math.min(this_min,heights[j]);
this_max=Math.max(this_max,(i-j+1)*this_min);
}
max=Math.max(max,this_max);
}
return max;
}
单调栈加哨兵
哨兵也就是我们添加的那两个0
public static int largestRectangle(int[] heights){
if (heights.length==0)
return 0;
if(heights.length==1)
return heights[0];
Deque<Integer> stack=new ArrayDeque<>();
stack.add(0);
//添加两个哨兵节点,用于第一个柱体和最后一个柱体的比较
int[] newHeights=new int[heights.length+2];
newHeights[0]=0;
newHeights[newHeights.length-1]=0;
int maxS=0;
System.arraycopy(heights,0,newHeights,1,heights.length);
//对添加了哨兵节点的数组进行遍历
for (int i = 1; i < newHeights.length; i++) {
while(newHeights[i]<newHeights[stack.peekLast()]){
//获取矩形高度,并将已经计算过的元素弹出
int a=newHeights[stack.pollLast()];
//获取矩形宽度
int b=i-stack.peekLast()-1;
maxS=Math.max(maxS,a*b);
}
stack.add(i);
}
return maxS;
}