力扣 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 等几个标量类型的变量,没有开辟额外的数组空间。空间复杂度达到了理论最优。
相关推荐
tankeven2 小时前
HJ158 挡住洪水
c++·算法
pan3035074792 小时前
在 Vue 3 + Vite 项目中覆盖 Element Plus 的默认样式
前端·vue.js·rust
Wect2 小时前
LeetCode 190. 颠倒二进制位:两种解法详解
前端·算法·typescript
刘永鑫Adam2 小时前
BiB | 蒋超实验室开发 Kun-peng(鲲鹏):实现可扩展且准确的泛域宏基因组分类
人工智能·算法·机器学习·分类·数据挖掘
ltl2 小时前
SM3 vs SHA-256:两个哈希函数的设计哲学与性能实测
后端·算法
知星小度S3 小时前
算法训练之递归(一)
数据结构·算法
Rust研习社3 小时前
Rust 的构建脚本是什么?今天一次性搞懂它
rust
未来之窗软件服务3 小时前
SenseVoicecpp ggml-webgpu大模型[AI人工智能(七十五)]—东方仙盟
c++·人工智能·算法·仙盟创梦ide·东方仙盟
ZPC82103 小时前
ROS 2 手眼标定完整方案
人工智能·算法·性能优化·机器人