跳到主要内容

43. 接雨水

题目描述

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

示例 1:

示例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 个单位的雨水(蓝色部分表示雨水)。

示例 2:

输入height = [4,2,0,3,2,5]

输出9

提示:

  • n==height.lengthn == height.length
  • 0<=n<=31040 <= n <= 3 * 10^4
  • 0<=height[i]<=105 0 <= height[i] <= 10^5

出处LeetCode

解法一:双指针

思路

可以使用双指针来解决这个问题。首先,我们注意到,只有当一个柱子的高度大于 00 时,它才能接雨水。因此,我们可以先找到最高的柱子,然后从左右两边向最高的柱子遍历,计算每个柱子能接的雨水量。

具体来说,我们可以使用两个指针 leftright,分别指向数组的两端。此时,我们可以维护两个变量 left_maxright_max,分别表示 left 左边的最高柱子高度和 right 右边的最高柱子高度。对于当前指向的柱子 height[i],它能接的雨水量取决于 min(left_max, right_max) - height[i]

代码

function trap(height: number[]): number {
let left = 0, right = height.length - 1;
let left_max = 0, right_max = 0;
let ans = 0;

while (left < right) {
if (height[left] < height[right]) {
if (height[left] >= left_max) {
left_max = height[left];
} else {
ans += left_max - height[left];
}
left++;
} else {
if (height[right] >= right_max) {
right_max = height[right];
} else {
ans += right_max - height[right];
}
right--;
}
}

return ans;
}

复杂度分析

  • 时间复杂度:O(n)O(n),其中 nn 是数组的长度。
  • 空间复杂度:O(1)O(1)

解法二:单调栈

思路

可以维护一个单调递减的栈栈 stack,栈中存放的是柱子的下标,初始时栈为空。对于每个柱子 height[i],我们不断地将 i 入栈,直到栈为空,或者当前柱子的高度小于等于栈顶柱子的高度。此时,我们将栈顶柱子的下标出栈,并计算当前柱子能接的雨水量。如果栈为空,我们继续将当前柱子的下标入栈。

代码

function trap(height: number[]): number {
let ans = 0;
const stack: number[] = [];

for (let i = 0; i < height.length; i++) {
while (stack.length > 0 && height[i] > height[stack[stack.length - 1]]) {
const top = stack.pop()!;
if (stack.length === 0) {
break;
}
const left = stack[stack.length - 1];
const width = i - left - 1;
const h = Math.min(height[i], height[left]) - height[top];
ans += width * h;
}
stack.push(i);
}

return ans;
}

复杂度分析

  • 时间复杂度:O(n)O(n),其中 nn 是数组的长度。
  • 空间复杂度:O(n)O(n)

解法三:动态规划

思路

可以使用动态规划来解决这个问题。我们可以维护两个数组 left_maxright_max,分别表示每个柱子左边的最高柱子高度和右边的最高柱子高度。对于每个柱子 height[i],它能接的雨水量取决于 min(left_max[i], right_max[i]) - height[i]

代码

function trap(height: number[]): number {
const n = height.length;
if (n === 0) {
return 0;
}

const left_max: number[] = new Array(n).fill(0);
const right_max: number[] = new Array(n).fill(0);

left_max[0] = height[0];
for (let i = 1; i < n; i++) {
left_max[i] = Math.max(left_max[i - 1], height[i]);
}

right_max[n - 1] = height[n - 1];
for (let i = n - 2; i >= 0; i--) {
right_max[i] = Math.max(right_max[i + 1], height[i]);
}

let ans = 0;
for (let i = 0; i < n; i++) {
ans += Math.min(left_max[i], right_max[i]) - height[i];
}

return ans;
}

复杂度分析

  • 时间复杂度:O(n)O(n),其中 nn 是数组的长度。
  • 空间复杂度:O(n)O(n)