1. 问题背景与核心挑战
这道LeetCode经典题目"接雨水"看似简单,实则暗藏多个考察维度。给定n个非负整数表示的高度图,计算按此排列的柱子能接多少雨水。表面上是计算凹槽面积,实则考察对数据结构的立体化理解。
我初次接触此题时,第一反应是暴力遍历每个柱子的左右最高点,但O(n²)的时间复杂度显然不够优雅。后来在面试中遇到变种题才明白,面试官真正想考察的是对空间复杂度的优化能力,以及能否发现双指针法的精妙之处。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 预处理解法:空间换时间的典型实践
2.1 左右最大值的预处理数组
核心思路是预先计算每个位置左右两侧的最大高度。创建两个数组leftMax和rightMax:
- leftMax[i]表示第i根柱子左侧(包括自身)的最高柱子
- rightMax[i]表示第i根柱子右侧(包括自身)的最高柱子
java复制int[] leftMax = new int[n];
leftMax[0] = height[0];
for (int i = 1; i < n; i++) {
leftMax[i] = Math.max(leftMax[i-1], height[i]);
}
int[] rightMax = new int[n];
rightMax[n-1] = height[n-1];
for (int i = n-2; i >= 0; i--) {
rightMax[i] = Math.max(rightMax[i+1], height[i]);
}
2.2 水位计算的关键公式
每个柱子上方的积水量由左右两侧最高柱子的较小值决定,这就是著名的"木桶原理":
java复制int water = 0;
for (int i = 0; i < n; i++) {
water += Math.min(leftMax[i], rightMax[i]) - height[i];
}
注意:当height[i]等于左右最大值时,计算结果为0,这与物理现象一致——柱子本身高度就是瓶颈时无法积水。
3. 双指针优化:空间复杂度的质变
3.1 从O(n)到O(1)的跨越
预处理解法需要额外的O(n)空间存储左右最大值。而双指针法则通过实时维护左右最大值,将空间复杂度降至O(1):
java复制int left = 0, right = height.length - 1;
int leftMax = 0, rightMax = 0;
int res = 0;
while (left < right) {
leftMax = Math.max(leftMax, height[left]);
rightMax = Math.max(rightMax, height[right]);
if (height[left] < height[right]) {
res += leftMax - height[left];
left++;
} else {
res += rightMax - height[right];
right--;
}
}
3.2 指针移动的底层逻辑
关键在于理解为什么可以安全移动较矮一侧的指针:
- 当height[left] < height[right]时,leftMax一定小于等于rightMax
- 此时left位置的水位由leftMax决定,因为右侧已经存在更高的柱子
- 反之亦然,形成完美的对称逻辑
4. 边界条件与特殊测试用例
4.1 空数组与单元素处理
java复制if (height == null || height.length <= 1) return 0;
4.2 单调递增/递减序列
例如[1,2,3,4]或[4,3,2,1]的积水量应为0,这是检验算法鲁棒性的好用例。
4.3 平台型地形
像[3,1,1,3]这样的用例,中间两个1应该积2单位水。许多初学者的代码会在平台处计算出错。
5. 算法可视化与复杂度分析
5.1 逐步演示例题
以height = [0,1,0,2,1,0,1,3,2,1,2,1]为例:
- 预处理阶段生成leftMax和rightMax数组
- 计算每个位置的min(leftMax,rightMax)-height
- 累加所有正值得到结果6
5.2 时间复杂度对比
| 方法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 暴力法 | O(n²) | O(1) |
| 预处理法 | O(n) | O(n) |
| 双指针法 | O(n) | O(1) |
6. 实际工程中的变种与应用
6.1 3D接雨水问题
这是2D问题的升级版,需要处理立体容器中的积水计算。通常采用优先队列+BFS的解法,时间复杂度升至O(nmlog(nm))。
6.2 实时水位监测系统
在IoT设备监控场景中,类似算法可用于计算低洼区域的潜在积水风险,但需要考虑实时数据流的处理。
6.3 内存分配优化
操作系统中的内存碎片整理算法,其核心思想与接雨水问题有异曲同工之妙——寻找可压缩的"凹槽"空间。
7. 面试中的考察重点
7.1 代码实现细节
面试官通常会关注:
- 边界条件处理是否完善
- 变量命名是否清晰
- 循环终止条件是否正确
7.2 算法优化思路
从暴力解法到最优解的推导过程,往往比最终答案更重要。建议在面试中明确说出自己的思考路径。
7.3 测试用例设计能力
优秀的候选人应该能主动提出各种边界用例,展示全面的思考维度。例如:
- 所有柱子等高的特殊情况
- 锯齿状极端地形
- 超大输入规模下的性能考虑
8. 个人实战经验分享
在真实面试中遇到这道题时,我首先用预处理法实现,然后主动提出:"这个解法需要O(n)额外空间,其实可以用双指针优化到O(1)"。面试官立即追问双指针的移动逻辑,这时对算法本质的理解就至关重要了。
另一个实用技巧:在白板编码时,先用注释写出算法步骤的伪代码,再填充具体实现。这既能帮助理清思路,也展示出良好的编码习惯。
