摘要:
很容易就联想到使用栈来解决。
题目
给定 $n$ 个非负整数表示每个宽度为 $1$ 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
按列计算(动态规划计算最值)
遍历每列,判断要不要计算列上的积水。
如何判断?计算出该列左侧最高列高度,计算右侧最高列高度。
若该列比二者都小,则该列需要计算;否则不需计算。
如何分别计算某列两侧最值?
可维护两个数组分别存储每列左右两侧最值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class Solution {
public int trap(int[] height) { int res = 0; int len = height.length; int[] leftSideMax = new int[len]; int[] rightSideMax = new int[len]; for (int i = 1; i < len - 1; i++) { leftSideMax[i] = Math.max(leftSideMax[i - 1], height[i - 1]); } for (int i = len - 2; i > 0; i--) { rightSideMax[i] = Math.max(rightSideMax[i + 1], height[i + 1]); } for (int j = 1; j < len - 1; j++) { int minimum = Math.min(leftSideMax[j], rightSideMax[j]); if (height[j] < minimum) { res += (minimum - height[j]); } } return res; } }
|
单调栈
核心思想是先降后增必有积水。
这个就有点像有效括号,一对儿。
维护一个单调递减的栈,栈中存入柱子下标。(为什么存入下标而不直接存入高度?因为我们可能会计算两个相隔若干柱子之间的距离。)
入栈:栈为空时;当前柱子低于栈顶柱子。
出栈:当前柱子高于栈顶柱子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
|
static const auto shutdown = []() { cin.tie(nullptr)->sync_with_stdio(false); return nullptr; }();
class Solution { public: static constexpr int N = 30010; pair<int, int> stk[N]; int tt = 0; int len; int res = 0;
int trap(vector<int>& height) { this->len = height.size();
for (int i = 0; i < len; i++) { if (!tt || height[i] <= stk[tt].second) { stk[++tt] = {i, height[i]}; continue; } for (; tt && height[i] > stk[tt].second;) { int stk_top_height = stk[tt].second; for (; tt; --tt) { if (stk[tt].second > stk_top_height) { int l = stk[tt].first, r = i; int lheight = stk[tt].second, rheight = height[i]; res += (r - l - 1) * (min(lheight, rheight) - stk_top_height); break; } } } stk[++tt] = {i, height[i]}; } return res; } };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Solution {
public int trap(int[] height) { int res = 0; Stack<Integer> stack = new Stack<>(); int len = height.length; for (int i = 0; i < len; i++) { while (!stack.isEmpty() && height[i] > height[stack.peek()]) { int heightInfoBeforePop = stack.peek(); stack.pop(); if (stack.isEmpty()) break; else { int stackTop = stack.peek(); res += ((i - stackTop - 1) * (Math.min(height[i], height[stackTop]) - height[heightInfoBeforePop])); } } stack.push(i); } return res; } }
|