【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;
    }
};
相关推荐
夏末蝉未鸣01几秒前
Sort-Merge Join【排序连接算法】详解(python代码实现,以FULL JOIN为例)
数据结构·算法
tjl521314_218 分钟前
01C++ 分离编译与多文件编程
前端·c++·算法
_日拱一卒9 分钟前
LeetCode:23合并K个升序链表
java·数据结构·算法·leetcode·链表·职场和发展
cany10009 分钟前
C++ -- 泛型编程
java·开发语言·c++
格林威12 分钟前
面阵相机 vs 线阵相机:堡盟与海康相机选型差异全解析 附C++ 实战演示
开发语言·c++·人工智能·数码相机·计算机视觉·视觉检测·工业相机
哆啦刘小洋12 分钟前
【LeetCode每日一题】:2033(贪心+快速排序魔改)
算法·leetcode
WolfGang00732115 分钟前
代码随想录算法训练营 Day48 | 图论 part06
算法·图论
cheems952719 分钟前
[算法手记] 动态规划 ,二维费用限制背包问题如何处理
算法·动态规划
Chase_______26 分钟前
LeetCode 1343 题解:定长滑动窗口经典入门题,从暴力枚举到高效优化一文搞懂
算法·leetcode·职场和发展
样例过了就是过了27 分钟前
LeetCode热题100 单词拆分
c++·算法·leetcode·动态规划·哈希算法