【LeetCode-42】接雨水

问题

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

示例

输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6

解答1:备忘录

class Solution {
public:
    int trap(vector<int>& height) {
        if (height.empty()) return 0;
        int n = height.size(), res = 0;
        int leftMax[n], rightMax = height.back();
        leftMax[0] = height[0];
        for (int i = 1; i < n - 1; i++) // 记录左边(包含自己)的最大值
            leftMax[i] = max(leftMax[i - 1], height[i]);
        for (int i = n - 2; i > 0; i--) {
            rightMax = max(rightMax, height[i]);
            res += min(rightMax, leftMax[i]) - height[i];
        }
        return res;
    }
};

重点思路

从左到右按列计算。每一列可以存放的水等于当前列左右最大高度的最小值,减去当前列的值,小于0则取0。左边的最大值可以在遍历列的时候顺便更新,只需要常数空间;而右边的最大值需要存放在数组rightMax中,该数组记忆当前列右边的最大值。rightMax比height数组大小小1,遍历是从第二列开始,到倒数第二列截止。

解答2:单调栈

class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size(), res = 0;
        stack<int> s; // 递减
        for (int i = 0; i < n; i++) {
            while (!s.empty() && height[s.top()] < height[i]) { // 一旦遇到比栈顶高的就计算这部分雨水
                int cur = s.top(); s.pop(); // 计算这部分雨水需要当前栈顶高度、弹出后栈顶高度,栈中存储的角标用于计算宽度
                if (s.empty()) break; // 因为左侧不会积水,所以直接跳过。如果左侧会积水,可以在数组首位添加INT_MAX高度的挡板。
                int l = s.top(), r = i;
                int w = r - l - 1;
                int h = min(height[r], height[l]) - height[cur];
                res += w * h;
            }
            s.push(i);
        }
        return res;
    }
};

重点思路

接雨水问题也是一个很经典的“下一个最大”问题。我们要计算一个位置能接多少雨水,需要该位置的前一个最大和后一个最大。前一个最大为当前位置弹出后的栈顶元素。

posted @ 2021-02-07 18:19  tmpUser  阅读(52)  评论(0编辑  收藏  举报