LeetCode 42 接雨水:双指针解法详解

解题思路

这道题是经典的接雨水 问题。核心是计算每个柱子位置能积多少水:对于位置 i,它能积的水量 = min(左边最高柱子, 右边最高柱子) - height[i](如果为正)。

方法一:动态规划(预处理左右最大值)

  • 用两个数组 leftMax[i]rightMax[i] 分别记录位置 i 左边和右边的最高高度。
  • 然后遍历一次计算每个位置的积水量。
  • 时间 O(n),空间 O(n)。

方法二:双指针(空间 O(1))

  • 利用双指针从两端向中间收缩,同时维护 leftMaxrightMax
  • 每次移动较矮一侧的指针,因为该侧的积水量只由该侧的 leftMaxrightMax 与当前高度决定(较矮一侧的 max 已经确定是短板)。
  • 时间 O(n),空间 O(1)。

这里采用更优的双指针法

Java 代码实现(双指针)

java 复制代码
class Solution {
    public int trap(int[] height) {
        // 边界情况:长度小于3时无法积水
        if (height == null || height.length < 3) {
            return 0;
        }
        
        int left = 0;                // 左指针
        int right = height.length - 1; // 右指针
        int leftMax = 0;            // 当前遍历过的左侧最高柱子
        int rightMax = 0;           // 当前遍历过的右侧最高柱子
        int totalWater = 0;         // 总积水量
        
        // 当左右指针未相遇时,持续计算
        while (left < right) {
            // 哪侧矮就处理哪侧,因为积水量由矮侧决定
            if (height[left] < height[right]) {
                // 左侧柱子较矮
                if (height[left] >= leftMax) {
                    // 当前柱子比左侧最高还要高,更新 leftMax,此时该柱子上方无法积水
                    leftMax = height[left];
                } else {
                    // 当前柱子低于左侧最高,说明可以积水
                    totalWater += leftMax - height[left];
                }
                left++; // 左指针右移
            } else {
                // 右侧柱子较矮(或相等)
                if (height[right] >= rightMax) {
                    // 当前柱子比右侧最高还要高,更新 rightMax
                    rightMax = height[right];
                } else {
                    // 可以积水
                    totalWater += rightMax - height[right];
                }
                right--; // 右指针左移
            }
        }
        
        return totalWater;
    }
}

关键点注释与解释

  • 双指针的核心思想 :积水的多少由两侧的"短板"决定。我们维护 leftMax(从左边看到当前位置的最高柱子)和 rightMax(从右边看到当前位置的最高柱子)。当 height[left] < height[right] 时,说明 leftMax 一定小于 rightMax(因为 rightMax 至少等于 height[right],而 height[right] > height[left])。此时 left 位置的积水量完全由 leftMax 决定,与右侧无关,因此可以先计算 left 处的水量,然后 left++。同理,右侧较低时,先处理右指针。
  • **为什么这样不会漏算?**我们每次都处理当前较矮的一侧,并且只依赖该侧的历史最大值。由于另一侧的柱子更高,保证了该侧的积水不会从另一端流走,因此计算的积水量是准确的。
  • 相等时移动哪边? height[left] == height[right] 时,移动哪边都可以,代码中归到了 else 分支(移动 right),不影响最终结果。
  • 复杂度分析:时间 O(n),每个元素最多访问一次;空间 O(1),只用了几个变量。

示例演示

height = [0,1,0,2,1,0,1,3,2,1,2,1] 为例:

步骤 left right heightleft heightright leftMax rightMax 操作 总水量
1 0 11 0 1 0 0 左矮,0<0? 更新leftMax=0,left++ 0
2 1 11 1 1 0 0 左矮,1>=0? 更新leftMax=1,left++ 0
3 2 11 0 1 1 0 左矮,0<1? 积水+1,left++ 1
4 3 11 2 1 1 0 右矮,处理右:1>=0? 更新rightMax=1,right-- 1
5 3 10 2 2 1 1 右矮,2>=1? 更新rightMax=2,right-- 1
6 3 9 2 1 1 2 右矮,1<2? 积水+1,right-- 2
7 3 8 2 2 1 2 右矮,2>=2? 更新rightMax=2,right-- 2
8 3 7 2 3 1 2 左矮,2>=1? 更新leftMax=2,left++ 2
9 4 7 1 3 2 2 左矮,1<2? 积水+1,left++ 3
10 5 7 0 3 2 2 左矮,0<2? 积水+2,left++ 5
11 6 7 1 3 2 2 左矮,1<2? 积水+1,left++ 6
12 7 7 相遇 - - - 结束 6

最终总水量为 6,与预期一致。

其他解法(动态规划)参考

如果追求思路简单,也可以用 O(n) 空间的方法:

java 复制代码
public int trap(int[] height) {
    if (height == null || height.length < 3) return 0;
    int n = height.length;
    int[] leftMax = new int[n];
    int[] rightMax = new int[n];
    
    leftMax[0] = height[0];
    for (int i = 1; i < n; i++) {
        leftMax[i] = Math.max(leftMax[i - 1], height[i]);
    }
    
    rightMax[n - 1] = height[n - 1];
    for (int i = n - 2; i >= 0; i--) {
        rightMax[i] = Math.max(rightMax[i + 1], height[i]);
    }
    
    int water = 0;
    for (int i = 0; i < n; i++) {
        int minBound = Math.min(leftMax[i], rightMax[i]);
        if (minBound > height[i]) {
            water += minBound - height[i];
        }
    }
    return water;
}

面试中常见法。

相关推荐
Jasmine_llq1 小时前
《P13016 [GESP202506 六级] 最大因数》
数据结构·算法
白狐_7981 小时前
考研408算法设计题保命策略:链表专题暴力解法精讲(2015 & 2019真题实战)
考研·算法·链表
fangpin1 小时前
GPU编程2: 并行策略
算法
碧海银沙音频科技研究院2 小时前
8基于BES2700IHC数字助听器系统开发
嵌入式硬件·算法
HZZD_HZZD2 小时前
智能电表选CoAP还是MQTT?合众致达万表实测:CoAP省流17.3%、续航提升38%,附报文拆解与选型矩阵
算法
linux-hzh3 小时前
百日算法修炼 · Day 05
java·算法
paopaokaka_luck3 小时前
基于springboot3+vue3的云南本土影视文旅推荐平台(协同过滤算法、Echarts图形化分析)
前端·spring boot·学习·算法·echarts·mybatis
程序员-Benothing3 小时前
MySQL 的存储引擎有哪些?它们之间有什么区别?
后端·mysql·面试·职场和发展
Ivanqhz3 小时前
泰勒展开(Taylor Expansion)
算法·决策树·机器学习·集成学习