力扣 42. 接雨水 - 高效双指针解法(Rust实现)详细题解

rust 复制代码
impl Solution {
    pub fn trap(height: Vec<i32>) -> i32 {
        if height.len()<3{
           return 0;
      }
    let mut left = 1;
    let mut right = height.len()-2;

    let mut leftmax = height[0];
    let mut rightmax = height[right+1];

    let mut res = 0;

    while left<=right{
        leftmax = leftmax.max(height[left]);
        rightmax = rightmax.max(height[right]);

        if leftmax<rightmax {
            res+=leftmax-height[left];
            left+=1;
        }else{
            res+=rightmax - height[right];
            right-=1;
        }
    }
    res
 }
}

💡 核心思想:木桶效应

要计算整个地形能接多少雨水,我们可以按 来计算。对于任意一列 i,它能接住的雨水量取决于:
它左边最高的柱子(leftmax)它右边最高的柱子(rightmax)较矮的那一个。

这就是经典的"木桶效应":
第 i 列的雨水量 = min(leftmax, rightmax) - height[i]
(注意:如果计算结果小于0,说明这列本身就是最高的,接不到水,记为0)

如果采用暴力或动态规划,我们需要 O(N)O(N) 的额外空间来存储每个位置的左右最大值。而双指针法通过在两端向中间逼近,巧妙地将空间复杂度降到了 O(1)O(1)。


🚀 代码逐段原理解析

1. 边界防御,防止越界

复制代码
if height.len() < 3 {
    return 0;
}

接雨水至少需要 3 根柱子(两边高,中间低)才能形成凹槽。这个前置判断不仅是逻辑优化,更重要的是保护了后续代码 。因为下面用到了 height.len() - 2,如果不拦截长度小于 3 的情况,在 Rust 中会因为 usize 溢出而引发 Panic。

2. 指针初始化

复制代码
let mut left = 1;
let mut right = height.len() - 2;

let mut leftmax = height[0];
let mut rightmax = height[right + 1]; // 即 height[height.len() - 1]

这是一个非常棒的细节优化!

最左侧 height[0] 和最右侧 height[len-1] 作为边界,它们上面是绝对不可能积水的。

所以你直接将它们作为初始的 leftmaxrightmax,并将 leftright 指针向内收缩一位(从 1len-2 开始遍历)。这省去了对无效边界的计算。

3. 双指针的核心

复制代码
while left <= right {
    // 1. 同步更新当前左右两侧的最大高度
    leftmax = leftmax.max(height[left]);
    rightmax = rightmax.max(height[right]);

    // 2. 比较 leftmax 和 rightmax,决定处理哪一边
    if leftmax < rightmax {
        res += leftmax - height[left];
        left += 1;
    } else {
        res += rightmax - height[right];
        right -= 1;
    }
}

这是整个算法的灵魂所在,为什么可以这样写?

  • 为什么可以只看一边的最大值?
    假设当前 leftmax < rightmax,对于 left 指针所在的位置来说,它的左侧最高确定是 leftmax。虽然它的右侧可能还有比 rightmax 更高的柱子,但这已经不重要了 ,因为"木桶"的短板已经确定是 leftmax。因此,我们可以放心地计算 left 处的积水量,并将 left 指针向右移。反之亦然。
  • 如何避免负数积水?
    注意这行代码:leftmax = leftmax.max(height[left]);
    因为你在计算积水 leftmax - height[left] 之前 ,先更新了 leftmax。这就保证了 leftmax 永远大于等于当前的 height[left]。如果当前柱子是迄今为止最高的,leftmax 就会等于 height[left],相减得 0,完美符合物理现实(高山顶上不积水),无需写繁琐的 if 去防止负数。

📊 复杂度分析

  • 时间复杂度:O(N)O(N)
    其中 NN 是数组 height 的长度。leftright 指针相向而行,每个元素最多被访问一次,整个过程只有一次遍历。
  • 空间复杂度:O(1)O(1)
    只使用了 leftrightleftmaxrightmaxres 等几个标量类型的变量,没有开辟额外的数组空间。空间复杂度达到了理论最优。
相关推荐
江畔柳前堤5 小时前
大语言模型分布式训练:从并行策略到万卡工程的系统梳理
人工智能·分布式·深度学习·算法·目标检测·机器学习·语言模型
Forever Nore6 小时前
LeetCode 4 寻找两个正序数组的中位数 - 二分
算法·leetcode
罗西的思考8 小时前
【OpenClaw具身硬件】MiniClaw 阅读笔记---(1)基础
人工智能·算法·机器学习
蛋先生DX9 小时前
大模型参数存储格式揭秘:BF不是男朋友
深度学习·算法·llm
爱跳舞的烤冷面9 小时前
自学嵌入式第22天(数据结构——哈希)
数据结构·算法·哈希算法
猎嘤一号9 小时前
博弈论(Game Theory)的理论、算法与工程
人工智能·算法·安全·博弈论
月光船幽幽10 小时前
影子模式下保护 logits 不被修改
人工智能·python·算法
马拉AI10 小时前
腾讯开源 Agent 记忆系统,AI“换对话就忘”的问题有了新解法(附安装使用教程)
人工智能·算法·开源·科研
地平线开发者11 小时前
征程6工具链模型X86推理方式说明
算法
地平线开发者11 小时前
【征程6】校准量化中HistogramObserver解析
算法