LeetCode-day6:接雨水

🌧️ 接雨水(Trapping Rain Water):从直觉到最优解的完整思考路径

题目来源 :LeetCode #42
难度 :Hard
关键词 :双指针|动态规划|单调栈|前缀最大值
一句话理解:给定一排柱子的高度,问下雨后能接住多少单位的雨水?

如果你第一次看到这道题时一脸茫然:"水怎么算?凭感觉?"------别担心,你不是一个人。但其实,只要抓住一个核心思想,这道"难题"就会变得异常清晰

今天,我将带你一步步拆解「接雨水」问题,从最朴素的暴力法出发,逐步优化到空间 O(1) 的双指针最优解,并解释为什么这样想是合理的


一、问题可视化:水到底怎么"接"?

输入示例:

python 复制代码
height = [0,1,0,2,1,0,1,3,2,1,2,1]

💡 关键观察:

  • 水不会从两边流走 → 必须有左右更高的柱子形成"凹槽"
  • 某个位置 i 能接多少水?
    取决于它左边最高的柱子和右边最高的柱子中较矮的那个

核心公式

python 复制代码
water[i] = max(0, min(left_max[i], right_max[i]) - height[i])

这个公式就是整道题的"灵魂"。


二、方法一:暴力法(O(n²))--- 理解问题的起点

对每个位置 i,我们向左、向右扫描,找出最大高度:

python 复制代码
def trap(height):
    total = 0
    n = len(height)
    for i in range(n):
        left_max = max(height[:i+1])
        right_max = max(height[i:])
        total += max(0, min(left_max, right_max) - height[i])
    return total

代码中left_max = max(height[:i+1])right_max = max(height[i:]) 的作用:

🎯 目标:对每个位置 i,找出:

  • 左边(含自己)最高的柱子left_max
  • 右边(含自己)最高的柱子right_max

🔍 详细解释:

height[:i+1] 是什么?
  • 这是 Python 的切片语法 ,表示从开头到索引 i包含 i
  • 因为切片 [a:b] 的规则是:左闭右开 → 包含 a,不包含 b
  • 所以 [:i+1] = 索引 0i(共 i+1 个元素)
  • ✅ 逻辑简单,容易验证
  • ❌ 时间复杂度 O(n²),大数据会超时

📌 价值:帮助我们确认"每个位置独立计算"的思路是正确的。


三、方法二:动态规划(O(n) 时间 + O(n) 空间)--- 预处理优化

既然每次都要找左右最大值,不如提前算好

步骤:

  1. 从左往右遍历,记录每个位置左侧(含自身)的最大高度 → left_max
  2. 从右往左遍历,记录每个位置右侧(含自身)的最大高度 → right_max
  3. 再遍历一次,用公式累加雨水
python 复制代码
def trap(height):
    if not height: return 0
    n = len(height)
    
    left_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 = [0] * n
    right_max[-1] = height[-1]
    for i in range(n-2, -1, -1):
        right_max[i] = max(right_max[i+1], height[i])
    
    total = 0
    for i in range(n):
        total += min(left_max[i], right_max[i]) - height[i]
    
    return total

代码中for i in range(n-2, -1, -1): 是什么意思?

这是 Python 中从后往前遍历的标准写法。

🔧 range(start, stop, step) 规则:

  • start 开始
  • stop 之前 停止(不包含 stop
  • 每次增加 step

所以:

python 复制代码
range(n-2, -1, -1)
  • start = n-2 :从倒数第二个元素开始(索引 n-2
  • stop = -1 :停在 -1 之前 → 最后一个有效索引是 0
  • step = -1:每次减 1(反向)
  • ✅ 时间 O(n),逻辑清晰
  • ✅ 面试中写出这个解法已经能拿高分
  • ❌ 需要额外 O(n) 空间存储两个数组

四、方法三:双指针(O(n) 时间 + O(1) 空间)--- 最优解!

🔑 核心洞察:

我们不需要同时知道 left_max 和 right_max

只要知道较小的那个就够了------因为水高由"短板"决定。

🤔 双指针策略:

  • 用两个指针 leftright 从两端向中间移动
  • 维护 left_maxright_max(当前已知的最大值)
  • 如果 height[left] < height[right]
    • 说明 left 这边是"短板"
    • 那么 left 位置的积水高度只由 left_max 决定(因为右边一定有更高的柱子兜底!)
    • 更新 left_max 或累加雨水,然后 left++
  • 否则,处理 right

✅ 代码实现(极简):

python 复制代码
def trap(height):
    if not height: return 0
    left, right = 0, len(height) - 1
    left_max = right_max = 0
    total = 0
    
    while left < right:
        if height[left] < height[right]:
            if height[left] >= left_max:
                left_max = height[left]
            else:
                total += left_max - height[left]
            left += 1
        else:
            if height[right] >= right_max:
                right_max = height[right]
            else:
                total += right_max - height[right]
            right -= 1
    
    return total
  • ✅ 时间 O(n),空间 O(1)
  • ✅ 一次遍历完成,效率最高
  • 💡 面试黄金解法

五、为什么双指针是安全的?------ 直觉解释

假设 height[left] < height[right]

  • 此时,right 侧至少有一个柱子(就是 height[right])比 left
  • 所以,left 位置的积水上限不会受右边更远柱子的影响 ------因为已经有 height[right] 这个"高墙"挡着了
  • 因此,只需关心 left 左边的历史最大值即可!

这就是"用已知的短板决定当前水量"的精妙之处。


六、总结:四种解法对比

方法 时间复杂度 空间复杂度 是否推荐
暴力法 O(n²) O(1) 仅用于理解
动态规划 O(n) O(n) ✅ 面试稳妥
双指针 O(n) O(1) ✅✅ 最优解,必会
单调栈 O(n) O(n) 适合变种题(如求积水区域)

七、我的收获

  1. 问题建模能力:把"接雨水"转化为"每个位置的积水高度计算"
  2. 预处理思想 :通过提前计算 left_max/right_max 避免重复扫描
  3. 双指针的高级用法:不只是"找和",还能"维护最值 + 利用对称性"
  4. 空间优化的艺术:通过逻辑推理,省去不必要的存储
相关推荐
没学上了15 小时前
VLM_一维离散卷积与二维离散卷积(还是复习感觉还行)
算法
黛色正浓15 小时前
leetCode-热题100-贪心合集(JavaScript)
javascript·算法·leetcode
轻微的风格艾丝凡16 小时前
嵌入式定时器计时技巧:用有符号数省略溢出判断的底层逻辑与实践
数据库·算法·dsp开发·嵌入式软件
No0d1es16 小时前
2025年12月 GESP CCF编程能力等级认证C++四级真题
算法·青少年编程·等级考试·gesp·ccf
CodeByV16 小时前
【算法题】快排
算法
一起努力啊~16 小时前
算法刷题--长度最小的子数组
开发语言·数据结构·算法·leetcode
rchmin16 小时前
限流算法:令牌桶与漏桶详解
算法·限流
leoufung16 小时前
LeetCode 221:Maximal Square 动态规划详解
算法·leetcode·动态规划
黑符石16 小时前
【论文研读】Madgwick 姿态滤波算法报告总结
人工智能·算法·机器学习·imu·惯性动捕·madgwick·姿态滤波