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] 作为边界,它们上面是绝对不可能积水的。
所以你直接将它们作为初始的 leftmax 和 rightmax,并将 left 和 right 指针向内收缩一位(从 1 和 len-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的长度。left和right指针相向而行,每个元素最多被访问一次,整个过程只有一次遍历。 - 空间复杂度:O(1)O(1)
只使用了left、right、leftmax、rightmax、res等几个标量类型的变量,没有开辟额外的数组空间。空间复杂度达到了理论最优。