代码随想录算法训练营四十二天|单调栈part02

LeetCode 42 接雨水

题目链接:42. 接雨水 - 力扣(LeetCode)

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例 1:

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]

输出:6

解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

示例 2:

输入:height = [4,2,0,3,2,5]

输出:9

首先要明确以下几个问题:

1.单调栈按行方向计算雨水容量,如图:

2.使用单调栈内元素的顺序:

从栈顶到栈底的顺序应该是从小到大的顺序。因为一旦发现添加的柱子的高度大于栈顶元素,说明出现凹槽,栈顶元素就是凹槽底部的柱子,栈顶第二个元素就是凹槽左边的柱子,新添加的柱子就是凹槽右边的柱子。

如图所示:

3.遇到相同高度的柱子怎么办,如图所示:

我们把栈顶元素先弹出,再将新元素加入栈中,因为高度相同,我们只使用最靠右边的柱子来计算即可。

根据以上分析,接下来就看单调栈的处理逻辑:

1.当前加入的元素高度小于栈顶元素的高度,直接将当前元素加入栈中即可;

2.当前加入的元素高度等于栈顶元素的高度,先将栈顶元素弹出,再将当前元素加入栈中即可;

3.当前加入的元素高度大于栈顶元素的高度:首先先将栈顶元素弹出,那么这个柱子就是凹槽的底部,它的高度记为mid;此时栈顶元素就是凹槽左边的柱子,当前加入的元素时凹槽右边的位置;所以雨水的高度 应该是:凹槽两边柱子的最小高度-mid宽度 是:凹槽右边的下标-凹槽左边的下标-1;所以体积就是:高度*宽度。

根据以上处理逻辑,给出代码:

java 复制代码
class Solution {
    public int trap(int[] height) {
        int n = height.length;
        int res = 0;
        Stack<Integer> st = new Stack<>();
        st.push(0);
        for (int i = 1; i < n; i++) {
            if (height[i] == height[st.peek()]) {
                st.pop();
            }
            while (!st.isEmpty() && height[i] > height[st.peek()]) {
                int mid = height[st.peek()];
                st.pop();
                if (!st.isEmpty())
                    res += (Math.min(height[i], height[st.peek()]) - mid) * (i - st.peek() - 1);
            }
            st.push(i);
        }
        return res;
    }
}

LeetCode 84 柱状图中最大的矩形

题目链接:84. 柱状图中最大的矩形 - 力扣(LeetCode)

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

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

示例 1:

输入:heights = [2,1,5,6,2,3]

输出:10

解释:最大的矩形为图中红色区域,面积为 10

示例 2:

输入: heights = [2,4]

输出: 4

依然要明确单调栈内元素的顺序:

从栈顶到栈底的顺序应该是从大到小的顺序。因为本题是要找到每个柱子左右两边第一个小于该柱子的柱子,和接雨水正好是相反的。

除了顺序,其他的逻辑和接雨水是差不多得,这里就不再赘述了。

代码如下:

java 复制代码
class Solution {
    public int largestRectangleArea(int[] heights) {
        int[] height=new int[heights.length+2];
        height[0]=0;
        for(int i=1;i<=heights.length;i++){
            height[i]=heights[i-1];
        }
        height[heights.length+1]=0;
        int n=height.length;
        int res=0;
        Stack<Integer> st=new Stack<>();
        st.push(0);
        for(int i=1;i<n;i++){
            if(height[i]==height[st.peek()])st.pop();
            while(!st.isEmpty()&&height[i]<height[st.peek()]){
                int mid=height[st.peek()];
                st.pop();
                if(!st.isEmpty()){
                    int left=st.peek();
                    int right=i;
                    res=Math.max(res,mid*(right-left-1));
                }
            }
            st.push(i);
        }
        return res;
    }
}

可以看到,在heights数组首位都加了一个元素0,这是为什么?

首先,在末尾加0是为了防止数组本身就是非递减的,那么这样一直到最后都没有进行res的计算,直接返回0,所以要在末尾加一个0。

其次,在开头加0是为了防止数组本身是非递增的,那么这样对于第一个柱子,我们无法得到left,所以要在开头加一个元素0。