力扣 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 等几个标量类型的变量,没有开辟额外的数组空间。空间复杂度达到了理论最优。
相关推荐
倒头就睡的小比特20 小时前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!20 小时前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
猎头南楼21 小时前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
旖旎夜光1 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark1 天前
大规模并行计算中的负载均衡算法研究4
算法
Because_of_Her11 天前
并查集-听课笔记
笔记·算法·并查集
码流子1 天前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构
another heaven1 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
wzdark1 天前
从算法设计模式看编程思维的抽象能力4
算法
孙启超1 天前
【AI开发之Rust】第 11 课:智能指针与内部可变性
开发语言·后端·rust