算法 - 接雨水
1. 题目
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例:
1 | 输入: height = [0,1,0,2,1,0,1,3,2,1,2,1] |
2. 解题思路
核心视角:按列计算。对每一列 i,它能接的水 = min(左侧最高, 右侧最高) - height[i](若为负则 0)。「木桶效应」取决于两边较矮的那个挡板。
2.1 动态规划预处理左右最大值
开两个数组 leftMax[i]、rightMax[i] 分别预处理「从两端到 i 的最大高度」,再逐列累加。O(n) 时间、O(n) 空间。
2.2 双指针(最优)
左右指针 l、r 向中间逼近,维护 leftMax、rightMax 两个标量:
比较
height[l]与height[r],较矮的一侧决定该侧列的接水量:- 若
height[l] < height[r]:左列的左挡板是leftMax,右挡板至少是height[r](≥ height[l]),所以左侧当前列水位由leftMax决定 → 累加leftMax - height[l],l++。 - 右侧对称处理。
- 若
边走边更新各自的 max。
时间复杂度:O(n),空间复杂度:O(1)。
3. TypeScript 实现
3.1 双指针(推荐)
1 | function trap(height: number[]): number { |
3.2 DP 预处理左右最大值
1 | function trapDP(height: number[]): number { |
4. 面试延伸
- 栈解法:用单调递减栈按「行」积水(遇到更高柱子就弹出凹槽结算宽度),思路另类,可作加分。
- 与盛最多水的容器对照:那道是「两条板一次盛水」,本题是「每列独立按左右挡板蓄水」,都是双指针经典,但计算方式不同。
- 追问:为什么
height[l] < height[r]时能直接对左列结算?因为右挡板已被height[r]保证不矮于当前左侧,水位由leftMax唯一确定,无需求真实的右侧最大值。
难度:困难 | LeetCode 42 | 双指针 / 单调栈