跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

LeetCode 42 接雨水算法详解:暴力、DP、双指针与单调栈四种解法

LeetCode 42 接雨水问题的四种解法。暴力法通过嵌套循环统计每柱接水量,易超时;动态规划预先记录左右最大值,将复杂度降至 O(n);双指针边遍历边更新极值,空间优化至 O(1);单调栈利用栈维护凹槽,高效定位存水区间。各方法层层递进,展现算法优化的核心思路。

ByteFlow发布于 2026/3/30更新于 2026/10/285 浏览
LeetCode 42 接雨水算法详解:暴力、DP、双指针与单调栈四种解法

LeetCode 42 接雨水详解

① 暴力解法

拿到这道题,我们首先看图分析。

不难发现当前位置装水的高度其实和两边高度有关。以索引为 5 的位置为例,最多装 2 个单位水,这取决于对应的左侧最大值和右侧最大值位置。

结论:i 位置处能装多少水取决于它左侧比它大的最大值与右侧比它大的最大值取 min 减去 i 位置高度即可。

公式:

int water = min(leftMax, rightMax) - height[i];

时间复杂度为 O(N^2),详细思路如下: 遍历一遍数组,当前位置能接的水的高度是左边最大值与右边最大值的 min 减去当前位置高度。累加每单位宽度的雨水面积。

代码如下:

class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size();
        int totalWater = 0;
        for (int i = 1; i < n - 1; i++) {
            int leftMax = 0, rightMax = 0;
            // 找左边最大值
            for (int j = 0; j < i; j++) {
                leftMax = max(leftMax, height[j]);
            }
            // 找右边最大值
            for (int j = i + 1; j < n; j++) {
                rightMax = max(rightMax, height[j]);
            }
            // 如果左右最大值有一个小于当前位置高度,则说明不能接雨水
            if ((leftMax < height[i]) || (rightMax < height[i])) continue;
            // 计算当前位置能接的水量
            int water = min(leftMax, rightMax) - height[i];
            if (water > 0) {
                totalWater += water;
            }
        }
        return totalWater;
    }
};

复杂度分析:

  • 时间复杂度:O(n²)
  • 空间复杂度:O(1)

② 动态规划解法

核心思想

对于每个位置 i,它能存多少水,取决于左边最高柱子和右边最高柱子中的较小者,再减去当前高度。

公式:

water[i] = max(0, min(leftMax[i], rightMax[i]) - height[i])
  • leftMax[i]:从 0 到 i 的最大高度(包含 i)
  • rightMax[i]:从 i 到 n-1 的最大高度(包含 i)
步骤
  1. 从左到右扫描,记录每个位置左边(含自己)的最大高度 → leftMax
  2. 从右到左扫描,记录每个位置右边(含自己)的最大高度 → rightMax
  3. 遍历每个位置,用上述公式累加雨水量
举例说明
height = [4, 2, 0, 3, 2, 5]
iheight[i]leftMax[i]rightMax[i]min(L,R)water[i]
044540
124542
204544
334541
424542
555550

总和:0 + 2 + 4 + 1 + 2 + 0 = 9

代码实现

先设置状态转移方程:dp_left[i] 表示 i 位置及 i 位置以左比大的最大高度;dp_right[i] 表示 i 位置及 i 位置以右比大的最大高度。

class Solution {
public:
    int trap(vector<int>& height) {
        vector<int> dp_left(height.size(), 0);
        vector<int> dp_right(height.size(), 0);
        
        dp_left[0] = height[0];
        for (int i = 1; i < height.size(); i++) {
            dp_left[i] = max(dp_left[i - 1], height[i]);
        }
        
        int n = height.size();
        dp_right[n - 1] = height[n - 1];
        for (int i = n - 2; i >= 0; i--) {
            dp_right[i] = max(dp_right[i + 1], height[i]);
        }
        
        int ans = 0;
        for (int i = 1; i < n - 1; i++) {
            int h = min(dp_left[i], dp_right[i]) - height[i];
            int w = 1;
            if (h > 0) ans += h * w;
        }
        return ans;
    }
};

复杂度分析:

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

③ 双指针解法

优化 DP 的空间复杂度至 O(1)。

双指针优化思路

使用两个指针一个从左往右走,一个从右往左走,直到相遇。如果左边比右边大,说明右边的话,肯定左边比它高(leftMax 一定大于它),而右边没有比 leftMax 大的,直接拿着 rightMax 去 right 位置进行比较获取新的 rightMax 即可,然后再与 right 位置处作差。

代码实现
class Solution {
public:
    int trap(vector<int>& height) {
        int left = 0, right = height.size() - 1;
        int leftMax = 0, rightMax = 0;
        int ans = 0;
        while (left <= right) {
            if (height[left] < height[right]) {
                leftMax = max(leftMax, height[left]);
                ans += leftMax - height[left];
                left++;
            } else {
                rightMax = max(rightMax, height[right]);
                ans += rightMax - height[right];
                right--;
            }
        }
        return ans;
    }
};

复杂度分析:

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

④ 单调栈解法

单调栈简介

单调栈是一种特殊的栈结构,栈内元素始终保持单调递增或单调递减的顺序。

  • 单调性:栈内元素要么一直增大,要么一直减小。
  • 出栈规则:新元素入栈时,若破坏单调性,则弹出栈顶元素,直到满足单调性再入栈。

主要用于解决'下一个更大/更小元素'类问题。

引入单调栈

根据暴力思路,优化为找左边离它最近比它大的以及右边离它最近比它大的。选择单调递减栈(即右边第一个比它大)。

只要小于 top 位置的值就一直入栈,直到大于的时候进行出栈处理(找到第一个凹槽处),从右往左求接入雨水的量。

代码实现
class Solution {
public:
    int trap(vector<int>& height) {
        int ans = 0;
        stack<pair<int, int>> s;
        s.push(make_pair(0, height[0]));
        int Min, w, h;
        for (int i = 1; i < height.size(); i++) {
            while (!s.empty() && height[i] > s.top().second) {
                int mid = s.top().second;
                s.pop();
                if (height[i] < mid) break;
                if (s.empty()) break;
                Min = min(s.top().second, height[i]);
                w = abs(i - s.top().first) - 1;
                h = Min - mid;
                if (w * h > 0) ans += w * h;
            }
            s.push(make_pair(i, height[i]));
        }
        return ans;
    }
};

复杂度分析:

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

总结

从暴力枚举到动态规划,再到双指针与单调栈,四种解法覆盖时间与空间复杂度的权衡。暴力法直观但低效,动态规划预处理打破嵌套循环,双指针进一步压缩空间,单调栈则以栈结构精准捕捉存水条件。掌握这些技巧,可灵活应对'区间极值'类问题。

目录

  1. LeetCode 42 接雨水详解
  2. ① 暴力解法
  3. ② 动态规划解法
  4. 核心思想
  5. 步骤
  6. 举例说明
  7. 代码实现
  8. ③ 双指针解法
  9. 双指针优化思路
  10. 代码实现
  11. ④ 单调栈解法
  12. 单调栈简介
  13. 引入单调栈
  14. 代码实现
  15. 总结

更多推荐文章

查看全部
  • MySQL CRUD 核心实战:增删改查全流程解析
  • 自定义 Shell 命令行解释器的实现与进程协作实践
  • Stable Diffusion v1.5 图片生成:主图/Banner/邮件头图
  • Redis 安装与配置教程 (Windows, Linux, macOS)
  • VMware 虚拟机安装与配置 Kali Linux 指南
  • Python 量化数据接口指南:baostock 获取分钟级 K 线教程
  • Spring Boot 数据可视化与图表集成实战
  • Flet:用 Python 构建跨平台桌面与 Web 应用
  • 大模型训练流水线并行(PP)性能评价指标与分析方法
  • 【深度解析】腾讯Claw三剑客横评:WorkBuddy、QClaw、CodeBuddy,3款AI Agent实测对比与选型指南
  • 豆瓣高分 Python 书籍推荐:从入门到实战精选
  • 服务端高并发分布式架构演进之路
  • Z-Image Turbo 本地部署与使用指南
  • 并发限流的常见实现方案与架构实践
  • MySQL MVCC 实现原理
  • 基于百度天气 API 的空气质量 WebGIS 可视化实践
  • Python 入门指南:核心优势、环境配置与最佳实践
  • ClawdBot 本地部署:零配置 Telegram AI 翻译机器人
  • AI 模型文件解析:v1-5-pruned-emaonly-fp16.safetensors 详解
  • Ubuntu 22.04 安装 ROS2 Humble 教程

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online