leetcode: 42. 接雨水
题目描述
给定 n 个非负整数表示每个宽度为 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 个单位的雨水(蓝色部分表示雨水)。
解题思路
当前位置能存多少雨水由它左侧、右侧最高柱子决定。
单调栈
- 单调栈 单调递减,出栈时累加两个柱子中间可以存放多少雨水(仅以这两柱子为边界)。
- 出栈元素为凹处 存放雨水
- 栈顶元素为左侧第一个更高柱子
- 待入栈元素为右侧第一个更高柱子
完整代码:
class Solution {
public int trap(int[] heights) {
int res = 0;
Deque<Integer> stk = new ArrayDeque<>();
for (int r = 0; r < heights.length; r++) {
while (!stk.isEmpty() && heights[stk.getFirst()] < heights[r]) {
int bottom = stk.removeFirst();
if (stk.isEmpty()) break;
int l = stk.getFirst();
res += (Math.min(heights[l], heights[r]) - heights[bottom]) * (r - l - 1);
}
stk.addFirst(r);
}
return res;
}
}
双指针
每个位置可以存储多少水,由它两侧 最高柱子中较矮的那根 决定。
双指针记录 每个位置 广义上的 左侧最高柱子和右侧最高柱子。
- e.g. 对于
l来说,lMax是它左侧实际最高柱子,rMax小于等于它右侧最高柱子。
完整代码:
class Solution {
public int trap(int[] heights) {
int n = heights.length;
int lMax = heights[0], rMax = heights[n - 1];
int l = 1, r = n - 2;
int res = 0;
while (l <= r) {
if (lMax < rMax) {
lMax = Math.max(lMax, heights[l]);
res += lMax - heights[l++];
}
else {
rMax = Math.max(rMax, heights[r]);
res += rMax - heights[r--];
}
}
return res;
}
}