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;
}

面试中常见法。

相关推荐
OPEN-F15 小时前
C++入门教程:数组、字符串与指针入门
数据结构·c++·算法
林森lsjs15 小时前
零基础吃透二叉树:定义、遍历与高频算法 —数据结构柒
java·开发语言·数据结构·算法·二叉树
船厂电气自动化ai大模型15 小时前
AI大模型与数学·第56课 快速傅里叶变换FFT:DFT高效优化算法,图像、音频、扩散模型工程加速核心工具
数据结构·人工智能·深度学习·算法·机器学习
203号居民15 小时前
【无标题】LeetCode hot 100 —54. 螺旋矩阵
算法
烤羊腿的程序员16 小时前
前端面试知识点大全(带答案)
前端·面试·职场和发展
ai产品老杨16 小时前
越界检测算法接入 AI 视频分析平台的流程与误报优化指南
人工智能·算法·音视频
bluesky96122216 小时前
malloc 和 realloc C/C++
c语言·c++·算法
M78佐菲16 小时前
Linux学习笔记:进程回收、exec函数簇与线程
linux·笔记·学习·算法
linux-hzh16 小时前
百日算法修炼 · Day 18
算法
用户8132679332516 小时前
Python 计算盘中 VWAP:为什么 1 分钟 K 线是量化工程中的高性价比选择
后端·算法·github