【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;
}
};
重点思路
接雨水问题也是一个很经典的“下一个最大”问题。我们要计算一个位置能接多少雨水,需要该位置的前一个最大和后一个最大。前一个最大为当前位置弹出后的栈顶元素。