给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

 

示例 1:

输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10

示例 2:

输入: heights = [2,4]
输出: 4

 

提示:

  • 1 <= heights.length <=105
  • 0 <= heights[i] <= 104
Related Topics
  • 数组
  • 单调栈
  • \n
  • 👍 1445
  • 👎 0
  • 题解

    直接说官方题解的思路,已测试用例【2,1,5,6,2,3】来说,在左侧-1,和6的索引上各新增一个虚拟节点,值为0。

    从左开始遍历,我们可以先在栈中压入一个-1作为打底。此处用Deque代替Stack使用,根据网友的说法Java 因为历史的原因 Stack 这个类设计得不好,官方建议使用Deque,只使用特定的方法从队位插入,从队尾弹出来模拟实现。具体可能需要看下Stack的底层源码实现。

    0)回归正题,当索引走到0的时候,对应的值2,对比栈顶当前索引-1对应的值0,2>0,那么我们直接把索引0压入栈。此时栈中情况为-1,0

    1)当索引到1的时候对应的值1,对比栈顶索引0,对应的值2,1<2,将2弹出,找到2高度为当前的2的时候能取得的最大面积的宽度,为当前的的索引1到当前栈顶0取出后的栈顶索引0之间的距离,为1。所以2对应的面积为2×1 = 2,对应索引0。

    在索引0弹出后,将索引1压入栈,当前栈中的情况为-1,1

    2)继续遍历下一个当前索引2,对应的值为5,对比栈顶索引1对应的值1,5大于1,所以直接压入栈。

    3)继续遍历下一个当前索引3,对应的值为6,对比当前栈顶索引2对应的值5,6大于5,直接压入栈。

    4)继续遍历下一个,当前索引4,对应的值2,2比6小,弹出栈顶索引3,并得到对应宽度为栈顶索引3弹出后的栈顶索引位置2,那么宽度就是4到2之间的距离个数4-2-1为1个,于是得到了当前位置的6对应的最大面积6×1=6。

    到目前位置已经知道的索引0位置的2对应的面积2,索引3位置的6对应的面积6。那么当前的最大面积就是6了。

    在索引3位置的6弹出后,当前索引4位置的2依旧还要和当前栈顶索引2的5对比。而5大于2,继续做相同的处理,将索引2弹出,栈中索引2的前一个索引是1,所以2位置的5对应的面积为5x(4-1-1) = 10。此时最大值面积为10。

    直到栈顶索引1对应的值1小于当前索引4对应的值2,将当前索引4压入栈

    5)下一个循环,当前索引5,对应的值3,比当前栈顶索引4对应的值2大,直接压入栈

    6)再下一个循环,数组部分的值已经结束,当前索引6,对应的值为我们之前虚拟的值0,而0小于3。弹出栈顶索引5,索引5对应的值为3,栈中索引5的前一个是索引4,那么可以知道索引5的高度3对应能取到的最大宽度为6-4-1 = 1,对应面积为3。当前最大面积依旧是10。

    在栈顶索引5弹出后,当前栈顶索引是4,对应的值2也大于0,继续弹出栈顶索引4,对应值2。同样的,栈顶索引4的前一个是1,当前位置的2能取得的最大宽度是6-1-1 = 4,最大面积为2×4 =8。最大面积还是10。

    索引4弹出后,继续往前对比索引1,1大于0,同样的操作。

    最终结束。

    下面是代码

    
    import java.util.ArrayDeque;
    import java.util.Deque;
    
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
    
        private int max = 0;
        private int[] arr;
        public int largestRectangleArea(int[] heights) {
            arr = heights;
            Deque<Integer> deque = new ArrayDeque<>();
            deque.offer(-1);
            for (int i = 0; i <= arr.length; i++) {
                int num = getNumByIndex(i);
                int dequeLastIndex = deque.peekLast();
                int dequeLastNum = getNumByIndex(dequeLastIndex);
                if (num >= dequeLastNum){
                    //这边deque.offer(i)和下面else里的deque.offer(i)其实可以合并,
                    // 将if判断条件取反执行else里的逻辑,并最终都if外deque.offer(i)
                    deque.offer(i);
                }else {
                    while (!deque.isEmpty() && getNumByIndex(deque.peekLast())>num){
                        dequeLastIndex = deque.pollLast();
                        //最终终止边界不好判断的话,可以直接在这里判断-1索引终止
                        if (dequeLastIndex == -1){
                            return max;
                        }
                        dequeLastNum = getNumByIndex(dequeLastIndex);
                        int length = i - deque.peekLast() - 1;
                        int area = dequeLastNum * length;
                        max = Math.max(area,max);
                    }
                    deque.offer(i);
                }
            }
            return max;
        }
    
        private int getNumByIndex(int index){
            if (index==arr.length || index==-1){
                return 0;
            }
            return arr[index];
        }
    }
    //leetcode submit region end(Prohibit modification and deletion)