【Hot 100 刷题计划】 LeetCode 42. 接雨水 | C++ 动态规划与双指针题解

LeetCode 42. 接雨水 | C++ 动态规划与双指针双解法题解

📌 题目描述

题目级别:困难 (Hard)

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

  • 示例 1:
    输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
    输出:6

💡 解题思路:核心物理定律 (木桶效应)

接雨水问题的核心在于**"化整为零"。我们不要从整体去考虑能接多少水,而是把目光聚焦在每一根单独的柱子**上。

问:下标为 i 的这根柱子,它的正上方到底能积攒多少水?

答:根据"木桶效应",这取决于它左边所有柱子中的最高点 ,以及右边所有柱子中的最高点 。

具体公式为:

第 i 列的水量 = min(左边最高, 右边最高) - 当前柱子的高度
(如果算出来是负数,说明当前柱子比两边都高,存不了水,取 0 即可)


🚀 解法一:前后缀分解 / 动态规划 (空间 O(N))

既然每一列都需要知道"左边最高"和"右边最高",我们可以提前把它们算出来存进数组里。

  1. 从左往右遍历一次,生成 left_max 数组,记录每个位置及其左边的最高高度。
  2. 从右往左遍历一次,生成 right_max 数组,记录每个位置及其右边的最高高度。
  3. 最后再遍历一次原数组,利用上面的公式计算水量并累加。

💻 C++ 代码实现 (标准规范版)

cpp 复制代码
class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size();
        if (n == 0) return 0;

        // 规范做法:使用 vector 代替变长数组
        vector<int> left_max(n);
        vector<int> right_max(n);

        // 1. 预处理左侧最大值数组
        left_max[0] = height[0];
        for (int i = 1; i < n; i++) {
            left_max[i] = max(left_max[i - 1], height[i]);
        }

        // 2. 预处理右侧最大值数组
        right_max[n - 1] = height[n - 1];
        for (int i = n - 2; i >= 0; i--) {
            right_max[i] = max(right_max[i + 1], height[i]);
        }

        // 3. 计算总积水量
        int res = 0;
        for (int i = 0; i < n; i++) {
            res += min(left_max[i], right_max[i]) - height[i];
        }

        return res;
    }
};

🏆 解法二:双指针 (时间 O(N),空间 O(1) 面试终极解)

解法一虽然快,但占用了额外的数组空间。能不能用 O(1)O(1)O(1) 的空间解决?答案是双指针。

我们在数组两端分别放置指针 left 和 right,同时用两个变量 l_max 和 r_max 来记录左右两边见过的最高高度。

既然决定水量的木板是短板,那么:

  • 如果 lmax<rmaxl_{max} < r_{max}lmax<rmax,说明左边这块板子更短。虽然我们不知道 right 指针以右还有没有更高的板子,但水桶的高度已经被左边的 lmaxl_{max}lmax 锁死了。此时我们可以放心地结算 left 指针所在列的水量,并将 left 向右移。
  • 反之,如果 lmax>=rmaxl_{max} >= r_{max}lmax>=rmax,说明右边是短板,高度被 rmaxr_{max}rmax 锁死。我们结算 right 指针所在列的水量,并将 right 向左移。

💻 进阶 C++ 代码实现

cpp 复制代码
class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size();
        if (n == 0) return 0;

        int left = 0, right = n - 1;
        int l_max = 0, r_max = 0;
        int res = 0;

        while (left < right) {
            // 更新左右两端的历史最高点
            l_max = max(l_max, height[left]);
            r_max = max(r_max, height[right]);

            // 谁是短板,谁就决定了当前列的水量,并向中间移动
            if (l_max < r_max) {
                // 左侧是短板,结算 left 处的水量
                res += l_max - height[left];
                left++;
            } else {
                // 右侧是短板,结算 right 处的水量
                res += r_max - height[right];
                right--;
            }
        }

        return res;
    }
};
相关推荐
HPtiger3 小时前
数据结构——选择排序
数据结构·算法·排序算法
康实训3 小时前
老年评估仿真实训平台架构设计与老年人能力评估实训室教学落地实践
大数据·人工智能·算法·实训室·实训室建设
DongQiShanRen3 小时前
六根硬件映射(四):归属澄清、两处不一致与卷十一总表
开发语言·人工智能·算法·rust·哈希算法·集成学习
CCblandy3 小时前
雪花 ID 四个边界实测:时钟回拨重号 500 个,前端 JSON.parse 直接丢精度
后端·python·算法·性能优化·架构
丑梨数据3 小时前
信创环境里跑文档工具:格式兼容与本地合规的几个坑
数据结构·人工智能·算法·决策树·排序算法
把我格子衫拿来3 小时前
VTK 9.5 与 Qt 6 联合开发:从嵌入三维窗口到可维护的桌面架构
c++·python·qt·vtk·三维可视化
无忧.芙桃4 小时前
C++语言原理与实践(十六):多态机制讲解
开发语言·c++·多态
erzong2315 小时前
Linux操作系统:线程概念与控制
linux·算法
wuminyu6 小时前
JMM学习总结:深入理解happens-before关系
java·linux·c语言·jvm·c++
Nil2086 小时前
leetcode 5最长回文子串
算法·leetcode·职场和发展