1
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。
图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。
示例:
输入: [2,1,5,6,2,3]
输出: 10
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/largest-rectangle-in-histogram
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
public int largestRectangleArea(int[] heights) {
//单调栈
//为什么用单调栈
//我们求某个高度的最大面积,往左边扫到第一个比它小的,再往右边扫到第一个比他小的,这中间的距离就是宽度,宽*高就是面积
//如果遍历数组,对每个高度都扫一遍,那么需要O(n^2)
//如果可以记录就好了,如果用一个单调递增的栈
//当遍历数组时,如果碰到比当前栈顶小的高度,那么也就是说这个下标之前的某些高度的面积是可以被确定的
//那么就可以把之前的这些高度弹出来,计算面积
//比如说,2,1,5,6,2,3,
//遍历到2时,2入栈,遍历到1时,1比2小,那么高度为2的面积是可以确定的,宽度就是1的下标和0的下标-1(这个0是什么意思,是假设2前面有个隐形的高度为0的矩形,就是之前说的往左扫第一个比高度2小的高度,这里
//因为2是第一个元素,没有,但是不代表不这么算(所以后面引入哨兵了)
//所以说栈中存的是下标,这样可以方便算宽度
//然后1入栈,然后5入栈,因为5比1大,所以1的面积是无法确定的
//后面6入栈,同样,1,5,的面积也无法确定
//接着2入栈,2比6小,而且因为我们维护着一个单调递增的栈,所以栈中,在6之前的元素肯定是小于等于6的(相等的情况下面再说)
//所以我们不用再往左边扫,找第一个比6小的高度,因为在6之前的那个元素就是比它小的,我们只要把6弹出来,然后计算一下6和2的下标差即可
//这里6出来过后,因为栈中还有可能有别的高度的面积可以被确定,所以2要继续和栈中元素比较,2比5小,所以5的面积也可以被确定
//因为单调栈,所以栈中5之后的元素肯定是比5大的,就像已经被弹出的6的元素一样,所以2肯定是他右边第一个比5小的元素,所以也可以直接计算宽度。
//5弹出后,1比2小了,所以比较停止,2入栈,继续这个遍历数组的流程。
//下面说说相等的情况,假设5后面有2个6,栈中现在是1.5,6,6.然后扫描到2了,第一个6出栈时候,计算出的宽度是1,此时算到的面积是6,而第二个6出栈后,因为存的小标,所以计算的宽度是2,此时算的面积是12
//所以并不会丢失最优解,可以不处理等于的情况,(这里的处理是说,弹出第一个6后,继续看栈顶是不是和6相等,是的话,继续弹,这样其实就是遇到重复值,少计算几次面积,只用最后一个6出来得到的宽度来求面积)
//总结来说,就是大于等于当前栈顶的元素都直接入栈,小于的需要把栈中比他大的都弹出(要维护这个递增栈)
//另外在高度数组头部和尾部添加两个值为0的哨兵,可以不用对栈空做单独判断,也不会在扫描结束后,栈中还有元素,还要单独处理栈中的元素
//数组头部加个0,这样处理一个高度时,因为0始终比所有高度小,所以栈永远不会为空全部弹完;数组尾巴加个0,因为0比所有高度都小,所以扫描到这个0的时候,可以把栈中所有元素弹出(第一个0除外)
LinkedList<Integer> stack = new LinkedList<>();
ArrayList<Integer> list = new ArrayList<>();
list.add(0);
for(int height : heights) list.add(height);
list.add(0);
stack.addLast(0);
int res = 0;//面积
for(int i = 1; i < list.size(); i++) {
while(list.get(i) < list.get(stack.peekLast())) {//注意这里是while!!
int heightPos = stack.pollLast();
int width = i - stack.peekLast()-1;//注意这里是减去stack.peekLast() i对应的是右边第一个比height小的下标,peekLast()代表的是左边第一个比height小的元素,因为是
//单调递增栈,所以height之前不会存在比他大的元素,所以他之前的元素就是他左边第一个比它小的元素
res = Math.max(res, width*list.get(heightPos));
}
stack.addLast(i);
}
return res;
}
}