LeetCode 第42题:接雨水
题目描述
给定 n
个非负整数表示每个宽度为 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.length
1 <= n <= 2 * 10^4
0 <= height[i] <= 10^5
解题思路
双指针法
这道题可以使用双指针法来解决,核心思想是:对于每个位置能接的雨水量取决于它左右两侧的最大高度中的较小值。
关键点:
- 每个位置能接的雨水量 = min(左侧最大高度, 右侧最大高度) - 当前高度
- 使用双指针可以优化空间复杂度,避免存储每个位置的左右最大高度
- 从两端向中间移动,保证较小的一端已经找到了真实的最大高度
具体步骤:
- 使用左右指针分别指向数组两端
- 记录左右两侧已遍历部分的最大高度
- 每次移动较小的那一侧的指针
- 累加当前位置能接的雨水量
图解思路
算法步骤分析表
步骤 | 操作 | 左指针 | 右指针 | 当前接水量 | 说明 |
---|---|---|---|---|---|
初始状态 | - | 0 | 11 | 0 | height=[0,1,0,2,1,0,1,3,2,1,2,1] |
第一步 | 移动左指针 | 1 | 11 | 0 | 左侧高度更小 |
第二步 | 计算位置1 | 2 | 11 | 0 | 无法接水 |
第三步 | 计算位置2 | 3 | 11 | 1 | 可以接1个单位水 |
状态/情况分析表
情况 | 输入 | 输出 | 说明 |
---|---|---|---|
基本情况 | [0,1,0,2,1,0,1,3,2,1,2,1] | 6 | 标准情况 |
单调递增 | [1,2,3,4,5] | 0 | 无法接水 |
对称情况 | [2,0,2] | 2 | 中间可以接水 |
代码实现
C# 实现
public class Solution {
public int Trap(int[] height) {
if (height == null || height.Length < 3) return 0;
int left = 0, right = height.Length - 1;
int leftMax = 0, rightMax = 0;
int result = 0;
while (left < right) {
// 更新左右两侧的最大高度
leftMax = Math.Max(leftMax, height[left]);
rightMax = Math.Max(rightMax, height[right]);
// 移动较小的一侧
if (leftMax < rightMax) {
result += leftMax - height[left];
left++;
} else {
result += rightMax - height[right];
right--;
}
}
return result;
}
}
执行结果
- 执行用时:84 ms
- 内存消耗:41.8 MB
代码亮点
- 🎯 使用双指针技巧,实现O(1)空间复杂度
- 💡 通过比较左右最大值来决定移动方向,避免重复计算
- 🔍 处理边界情况:数组长度小于3时直接返回0
- 🎨 代码结构简洁,逻辑清晰,易于理解
常见错误分析
- 🚫 没有处理数组为空或长度不足的边界情况
- 🚫 错误计算最大高度,导致结果偏大
- 🚫 移动指针的条件判断错误
- 🚫 忘记更新最大高度值
解法对比
解法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
---|---|---|---|---|
暴力法 | O(n²) | O(1) | 思路简单 | 效率低 |
动态规划 | O(n) | O(n) | 直观易懂 | 空间复杂度高 |
双指针法 | O(n) | O(1) | 最优解 | 理解稍难 |