文章链接:42. 接雨水84. 柱状图中最大的矩形

视频链接:42. 接雨水84.柱状图中最大的矩形

题目链接:42. 接雨水84.柱状图中最大的矩形


42.接雨水

思路一(双指针):

当前列雨水面积:min(左边柱子的最高高度,记录右边柱子的最高高度) - 当前柱子高度。

当前位置左边的最高高度:是前一个位置的左边最高高度和本高度的最大值。

class Solution {
public:
    int trap(vector<int>& height) {
        if (height.size() <= 2) return 0;
        vector<int> maxLeft(height.size(), 0);
        vector<int> maxRight(height.size(), 0);
        int size = maxRight.size();

        // 记录每个柱子左边柱子最大高度
        maxLeft[0] = height[0];
        for (int i = 1; i < size; i++) {
            maxLeft[i] = max(height[i], maxLeft[i - 1]);
        }
        // 记录每个柱子右边柱子最大高度
        maxRight[size - 1] = height[size - 1];
        for (int i = size - 2; i >= 0; i--) {
            maxRight[i] = max(height[i], maxRight[i + 1]);
        }
        // 求和
        int sum = 0;
        for (int i = 0; i < size; i++) {
            int count = min(maxLeft[i], maxRight[i]) - height[i];
            if (count > 0) sum += count;
        }
        return sum;
    }
};


思路二(单调栈):

该题需要用单调栈求出遍历到的元素的左右两边的第一个比所遍历到的元素大的元素,从而求出当前凹槽雨水的体积。

单调栈的处理逻辑:

1.当前遍历的元素(柱子)高度小于栈顶元素的高度 height[i] < height[st.top()]:把这个元素加入栈中;

2.当前遍历的元素(柱子)高度等于栈顶元素的高度 height[i] == height[st.top()]:直接加入栈中,或者,将栈顶元素弹出后再将元素加入栈中;

3.当前遍历的元素(柱子)高度大于栈顶元素的高度 height[i] > height[st.top()]:取栈顶元素并弹出,这个就是凹槽的底部(即中间位置),下标记为mid,对应的高度为height[mid];而凹槽的左边就是此时的栈顶元素st.top();凹槽右边的位置就是遍历到的元素。

凹槽雨水体积的计算公式如下:

雨水的高度:是 min(凹槽左边高度, 凹槽右边高度) - 凹槽底部高度;

雨水的宽度:是 凹槽右边的下标 - 凹槽左边的下标 - 1


class Solution {
public:
    int trap(vector<int>& height) {
        if (height.size() <= 2) return 0; // 两个柱子接不了水
        stack<int> st;
        st.push(0);
        int sum = 0;
        for (int i = 1; i < height.size(); i++) {
            if (height[i] <= height[st.top()]) { // 情况一和二
                st.push(i);
            } else { // 情况三
                while (!st.empty() && height[i] > height[st.top()]) { // 是一直比
                    int mid = st.top();
                    st.pop();
                    if (!st.empty()) { // 指的是左边有比heigt[mid]大的元素(即出现凹槽)
                        int h = min(height[st.top()], height[i]) - height[mid];
                        int w = i - st.top() - 1;
                        sum += h * w;
                    }
                }
                st.push(i);
            }
        }
        return sum;
    }
};


84.柱状图中最大的矩形

思路:

与上一题的思路一模一样,就是栈内元素顺序不同,因为本题要寻找左右两边比遍历到的元素矮的。所以要用单调递减栈(从栈头找栈底)。

本题只写 “单调栈” 写法。

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int result = 0;
        stack<int> st;
        heights.insert(heights.begin(), 0); // 数组头部加入元素0
        heights.push_back(0); // 数组尾部加入元素0
        st.push(0);
        for (int i = 1; i < heights.size(); i++) {
            if (heights[i] >= heights[st.top()]) { // 情况一和二
                st.push(i);
            } else { // 情况三
                while(!st.empty() && heights[i] < heights[st.top()]) {
                    int mid = st.top();
                    st.pop();
                    if (!st.empty()) {
                        int left = st.top();
                        int right = i;
                        int w = right - left - 1;
                        result = max(result, w * heights[mid]);
                    }
                }
                st.push(i);
            }
        }
        return result;
    }
};

:为什么要在头和尾加入元素0?

尾:如果数组本身就是升序的,那么入栈之后 都是单调递减,一直都没有走 情况三 计算结果的哪一步,所以最后输出的就是0了。

头:如果数组本身是降序的,例如 [8,6,4,2],在 8 入栈后,6 开始与8 进行比较,此时我们得到 mid(8),rigt(6),但是得不到 left。

因为 将 8 弹出之后,栈里没有元素了,那么为了避免空栈取值,直接跳过了计算结果的逻辑。

之后又将6 加入栈(此时8已经弹出了),然后 就是 4 与 栈口元素 8 进行比较,周而复始,那么计算的最后结果resutl就是0。