1. 项目概述
"接雨水"是力扣HOT100系列中的一道经典算法题(编号T.42),考察对数组处理、动态规划和双指针等核心算法思想的掌握。这道题要求计算给定高度数组能接住多少雨水,是面试中高频出现的题目类型。
我在刷题过程中发现,这道题主要有两种主流解法:辅助数组法和双指针法。前者思路直观但空间复杂度较高,后者则需要更巧妙的指针操作但能实现O(1)空间复杂度。本文将详细拆解这两种解法的实现逻辑,并分享我在力扣打卡过程中的调试心得和优化技巧。
2. 核心算法解析
2.1 问题建模与暴力解法
给定n个非负整数表示的高度图,计算按此排列的柱子能接住多少雨水。示例输入:[0,1,0,2,1,0,1,3,2,1,2,1],输出:6。
暴力解法的思路是:对于每个位置i,找到其左边最高柱子left_max和右边最高柱子right_max,该位置接水量为min(left_max, right_max) - height[i]。时间复杂度O(n²),显然不适用于大规模数据。
# 暴力解法伪代码 result = 0 for i in range(n): left_max = max(height[:i+1]) right_max = max(height[i:]) result += min(left_max, right_max) - height[i]2.2 辅助数组法优化
通过预处理两个辅助数组,可以避免重复计算:
- left_max[i]:记录位置i左侧(含i)的最大高度
- right_max[i]:记录位置i右侧(含i)的最大高度
def trap(height): if not height: return 0 n = len(height) left_max = [0] * n right_max = [0] * n left_max[0] = height[0] for i in range(1, n): left_max[i] = max(left_max[i-1], height[i]) right_max[-1] = height[-1] for i in range(n-2, -1, -1): right_max[i] = max(right_max[i+1], height[i]) res = 0 for i in range(n): res += min(left_max[i], right_max[i]) - height[i] return res注意事项:边界条件处理很重要。当height为空时直接返回0,且辅助数组的初始化需要包含首尾元素。
2.3 双指针法进阶
双指针法通过动态维护左右两端的最大值,将空间复杂度优化到O(1):
def trap(height): if not height: return 0 left, right = 0, len(height)-1 left_max = right_max = res = 0 while left < right: if height[left] < height[right]: if height[left] >= left_max: left_max = height[left] else: res += left_max - height[left] left += 1 else: if height[right] >= right_max: right_max = height[right] else: res += right_max - height[right] right -= 1 return res关键点在于:
- 左右指针从两端向中间移动
- 每次移动较矮一侧的指针(因为接水量由较矮的一侧决定)
- 动态更新左右最大值并计算当前列的接水量
3. 算法对比与选择
| 方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| 暴力解法 | O(n²) | O(1) | 仅用于理解问题本质 |
| 辅助数组法 | O(n) | O(n) | 面试首选,思路最直观 |
| 双指针法 | O(n) | O(1) | 要求优化空间复杂度时用 |
实际面试中,建议先阐述辅助数组法,再优化到双指针法。我在力扣提交记录显示,双指针法运行时间通常比辅助数组法快10-15%,内存消耗减少约50%。
4. 调试与优化实录
4.1 常见错误排查
数组越界:忘记处理空数组情况导致index error
# 错误示例 left_max[0] = height[0] # 当height=[]时崩溃逻辑错误:混淆左右最大值的更新时机
# 错误示例(双指针法) if height[left] >= left_max: res += left_max - height[left] # 应该在else分支边界遗漏:未考虑首尾柱子不能接水的特性
4.2 性能优化技巧
提前终止:当left_max <= height[right]时,右侧不需要再计算
while left < right and left_max <= height[right]: left_max = max(left_max, height[left]) res += left_max - height[left] left += 1合并循环:辅助数组法的两个预处理循环可以合并(但会降低可读性)
使用内置函数:max()比if判断稍快,但差异不大
5. 力扣刷题方法论
这道题在HOT100中编号12/100,属于"数组与字符串"类别。我的刷题策略是:
- 先独立思考15分钟,尝试暴力解法
- 查看题解学习最优解法
- 手写实现并提交
- 记录不同解法的时空复杂度
- 制作错题本记录典型错误
对于接雨水这类经典问题,建议:
- 理解"木桶效应"的核心思想
- 掌握从暴力解法到优化解法的演进过程
- 熟练手写双指针法的代码模板
我在实现过程中发现,画图辅助理解非常有效。用柱状图标注左右最大值和接水区域,能直观验证算法正确性。例如对于输入[4,2,0,3,2,5],可以这样可视化:
X X X X X X X X X X X通过这道题的练习,我对双指针法的应用场景有了更深理解——当问题可以分解为左右两部分的相互作用时,双指针往往是优化空间复杂度的利器。