LeetCode 84. 柱状图中最大的矩形

https://leetcode-cn.com/problems/largest-rectangle-in-histogram/

 

这个题没有做啥要求,暴力也能过。

其实这个题有点像接雨水一样,看题解有人用扫描法也做出来了。但是还是用单调栈的方法吧,(明明22天前做出来了,今天却做不出来???)

    public int largestRectangleArea(int[] heights) {
        LinkedList<Integer> stack = new LinkedList<>();
        int max = 0;
        for(int i = 0; i < heights.length; i++){
            while(!stack.isEmpty() && heights[stack.getLast()] > heights[i]){
                int j = stack.removeLast();
                max = Math.max(max, heights[j] * (stack.isEmpty()?i:(i-stack.getLast() - 1)));
            }
            stack.addLast(i);
        }
        while(!stack.isEmpty()){
            int j = stack.removeLast();
            max = Math.max(max, heights[j] * (stack.isEmpty()?heights.length:(heights.length-stack.getLast() - 1)));
        }
        return max;
    }

 

其实还是很容易理解的,我们维护一个递增栈,如果遇到一个比栈顶元素要小的值,就开始计算最大面积。

我们把数组下标抽象成一排木板并排,然后数组下标就是木板左下角的坐标。

遇到一个小的元素,我们就依次计算栈顶元素能维护到的最大面积。

上图我们第一个遇到递减的情况是数组下标为1的时候,那么我们栈中只有一个元素0,我们把它出栈。

新面积计算的方法:栈顶元素的木板长度 * (如果栈为空,那么就是当前数组下标,否则就是 当前数组下标- 栈顶元素下标 - 1);

然后我们去比较max和新面积的大小。

我们重复这个步骤,直到栈为空,或者栈顶元素比当前下标元素还要小的情况

当然这种方法有可能会出现2,1,5,6,7,8后面全部都是递增的情况,我们就需要在数组的最后边再添加一个长度为0的木板,由于java传的是数组我不想去复制一次,所以就再扫一次栈好了。

总的来说,我对做过的题有印象,但是有些题目记忆不是很深刻,这题单调栈和全递增的情况后面我都想出来了,就是没有处理好面积计算的时候栈为空的情况。看来还是得多回忆一下老题。

看到一个单调栈的总结,复制过来记忆一下。LeetCode上单调栈的问题也很多,像接雨水,最大矩形面积,最大正方形面积这种,这个也是比较考验思维的一种题,希望自己可以多回忆一下。

// 明确单调栈能够解决的问题

// 1.找右侧第一个更小 增栈

// 2.找右侧第一个更大 减栈

// 3.找左侧第一个更小 增栈

// 4.找左侧第一个更大 减栈

 

2020年9月14日更新


其实这个题和之前接雨水的两个题一样,都是可以用双指针去完成的。

我们只需要维护left和right数组即可。

这里的数组指的是 left[i]:第i个柱子左手第一个比当前柱子小的下标。

        right[i]: 第i个柱子右手第一个比当前柱子小的下标。

我们计算max的方法就是 (right[i] - left[i] - 1) * height[i]。

class Solution {
    public int largestRectangleArea(int[] heights) {
        /*
            使用 left 和 right 数组 记录 从 i 左右两边延伸,第一个比 i 位置小的柱子索引位置
            那么第 i 根柱子能组成面积的宽度就是 right[i] - left[i] - 1
        */
        int n = heights.length;
        if(n == 0){
            return 0;
        }
        int[] left = new int[n];
        int[] right = new int[n];

        //第一根柱子,左边不存在比它小的
        left[0] = - 1;
        //最后一根柱子,右边不存在比它小的
        right[n - 1] = n;

        for(int i = 1; i < n; i++){
            int temp = i - 1;
            while(temp >= 0 && heights[temp] >= heights[i]){
                temp = left[temp];
            }
            //当上述循环 break 后,  temp 即为左边第一根小于 i 位置的柱子
            left[i] = temp;
        }

        for(int i = n - 2; i >= 0; i--){
            int temp = i + 1;
            while(temp < n && heights[temp] >= heights[i]){
                temp = right[temp];
            }
            //当上述循环 break 后,  temp 即为右边第一根小于 i 位置的柱子
            right[i] = temp;
        }

        int maxArea = 0;
        for(int i = 0; i < n; i++){
            int width = right[i] - left[i] - 1;
            maxArea = Math.max(maxArea, heights[i] * width);
        }
        return maxArea;
    }
}

 

posted @ 2020-05-30 09:51  ZJPang  阅读(171)  评论(0编辑  收藏  举报