
这题本质就是算每一个柱子上能存多少水,全部累加;
单调栈
单调栈与下面两个是不一样的思路,但是性能又不如下面
单调栈是按层(横着一层一层)算水。
栈里存下标,并且保证从栈底到栈顶高度递减。你可以把栈想成一堵正在往左墙堆的墙,墙是越堆越低的。
遇到当前柱子比栈顶高时,说明栈顶那根柱子(mid)左右两边都有更高的墙把它夹住了,它上面就能存水:
class Solution {
public int trap(int[] height) {
Stack<Integer> stack = new Stack<>();
int res = 0;
for (int i = 0; i < height.length; i++) {
// 当前柱子比栈顶高,栈顶形成凹槽,可以积水
while (!stack.isEmpty() && height[i] > height[stack.peek()]) {
int mid = stack.pop(); // 凹槽底部柱子
if (stack.isEmpty()) break; // 左边没有挡板,接不住水
int l = stack.peek(); // 左边界柱子下标
int h = Math.min(height[l], height[i]) - height[mid]; // 积水高度
int w = i - l - 1; // 积水宽度
res += h * w; // 累加这一层的水
}
stack.push(i); // 当前柱子入栈
}
return res;
}
}
动态规划
对于每个位置 i,它能接的雨水取决于 min(左边最高柱子, 右边最高柱子) - heighti
所以用一个数组记录左最高 另一个记录有最高就可以了
class Solution {
public int trap(int[] height) {
int n = height.length;
int[] maxl = new int[n];
int[] maxr = new int[n];
maxl[0] = height[0];
for (int i = 1; i < n; i++) {
maxl[i] = Math.max(maxl[i - 1], height[i]);
}
maxr[n - 1] = height[n - 1];
for (int i = n - 2; i >= 0; i--) {
maxr[i] = Math.max(maxr[i + 1], height[i]);
}
// 每个位置积水 = min(左最高, 右最高) - 当前柱子高度
int res = 0;
for (int i = 0; i < n; i++) {
res += Math.min(maxl[i], maxr[i]) - height[i];
}
return res;
}
}
双指针解法
当前位置蓄水量 = min (左边最高,右边最高) - 当前柱子高度,如果结果 <=0 就存不了水,加 0
class Solution {
public int trap(int[] height) {
int l = 0;
int r = height.length - 1;
int maxl = 0;
int maxr = 0;
int sumwater = 0;
while (l != r) {
if (height[l] < height[r]) {
// 左边当前柱子更矮,水位由左边已经见过的最高柱子maxl决定
if (height[l] < maxl) {
sumwater += maxl - height[l]; // 当前柱子比左最高矮,能存水
} else {
maxl = height[l]; // 当前柱子更高,更新左最高,存不了水
}
l++;
} else {
// 右边当前柱子更矮,水位由右边已经见过的最高柱子maxr决定
if (height[r] < maxr) {
sumwater += maxr - height[r]; // 当前柱子比右最高矮,能存水
} else {
maxr = height[r]; // 当前柱子更高,更新右最高,存不了水
}
r--;
}
}
return sumwater;
}
}
时间复杂度: O(N)
空间复杂度: O(1)