当前位置: 首页 > news >正文

Leetcode力扣解题记录--第42题 接雨水(动规和分治法)

题目链接:42. 接雨水 - 力扣(LeetCode)

这里我们可以用两种方法去解决巧妙地解决这个题。首先来看一下题目

题目描述

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

示例 1:

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 

示例 2:

输入:height = [4,2,0,3,2,5]
输出:9

提示:

  • n == height.length

  • 1 <= n <= 2 * 104

  • 0 <= height[i] <= 105

题目作答

核心解题思想

        解决此问题的根本在于,对于数组中的任意一个位置 i,其上方能够积蓄的雨水高度,取决于它左侧所有柱子中的最高者 (left_max) 和右侧所有柱子中的最高者 (right_max) 中较矮的那一个。这个较矮的高度决定了该位置的水位。

  • 该位置 i 的最终水位高度为:water_level = min( left_max, right_max)。
  • 该位置 i 本身能存储的雨水量为:water[i] = water_level - height[i]。(如果结果为负数,则该处蓄水量为0)

我们的目标就是求出所有位置的蓄水量的值再相加


方法一:动态规划

此方法通过预计算,直观地实现了上述核心思想。

思路

  1. 预计算左侧最大高度:创建一个数组 left_max_arr,长度与 height 相同。从左到右遍历 height 数组,对于每个位置 i,left_max_arr[i] 存储从索引 0 到 i 的所有柱子中的最大高度。
  2. 预计算右侧最大高度:创建另一个数组 right_max_arr。这一次,我们从右到左遍历 height 数组,对于每个位置 i,right_max_arr[i] 存储从索引 i 到 n-1 的所有柱子中的最大高度。
  3. 计算总雨水量:当我们拥有了每个位置的左侧最高和右侧最高之后,就可以进行第三次遍历。对于每个位置 i,其水位就是 min(left_max_arr[i], right_max_arr[i])。用这个水位减去当前柱子的高度 height[i],就是该位置的蓄水量。将所有位置的蓄水量累加起来,即为最终答案。

图片来源:毒瘤面试题:接雨水_哔哩哔哩_bilibili

复杂度分析

  • 时间复杂度: O(n)。我们进行了三次独立的线性遍历,总时间复杂度为 O(n)+O(n)+O(n)=O(n)。
  • 空间复杂度: O(n)。我们使用了两个长度为 n 的额外数组 left_max_arr 和 right_max_arr 来存储中间计算结果。

代码如下

class Solution {
public:int trap(vector<int>& height) {int n = height.size();if (n < 3) {return 0;}// 1. left_max_arr 数组,记录从左到右的最大值vector<int> left_max_arr(n);left_max_arr[0] = height[0];for (int i = 1; i < n; ++i) {left_max_arr[i] = max(left_max_arr[i - 1], height[i]);}// 2. right_max_arr 数组,记录从右到左的最大值vector<int> right_max_arr(n);right_max_arr[n - 1] = height[n - 1];for (int i = n - 2; i >= 0; --i) {right_max_arr[i] = max(right_max_arr[i + 1], height[i]);}// 3. 遍历每个位置,计算并累加雨水int total_water = 0;for (int i = 0; i < n; ++i) {// 计算当前位置的水位int water_level = min(left_max_arr[i], right_max_arr[i]);// 累加当前位置的蓄水量total_water += water_level - height[i];}return total_water;}
};

方法二:分治法

此方法是动态规划解法的空间优化版本,它在一次遍历中就完成了所有计算,无需额外的存储数组。

思路

第一步:找到全局最高点

        首先,我们需要一次遍历整个 height 数组,找到其中最高的柱子的高度 max_height 和其对应的索引 max_index。这个最高的柱子就像一座山峰,它自然地将整个地形分成了左、右两个部分。在它左边的区域,积水情况最多只会受到它本身以及它左边的墙的影响;同理,右边的区域也是如此。

第二步:处理左半部分(从数组开头到最高点)

        现在,我们从数组的最左边(索引 0)开始,向右遍历直到最高点所在的索引 max_index。

  • 我们维护一个变量 left_max,用来记录从左边到当前位置为止遇到的最高墙体。
  • 对于这个区间的任何一根柱子 height[i],它右边的最高墙一定是我们第一步找到的全局最高点 max_height。
  • 因此,在该位置 i 的蓄水高度瓶颈,就完全取决于其左边的最高墙 left_max。因为 left_max 不可能超过 max_height。
  • 所以,在位置 i 的蓄水量就是 left_max - height[i]。
  • 我们从左向右遍历,不断更新 left_max 并累加每个位置的蓄水量。

第三步:处理右半部分(从数组末尾到最高点)

        处理完左边,我们用完全对称的逻辑来处理右半部分。

  • 我们从数组的最右边(索引 n-1)开始,向左遍历直到最高点所在的索引 max_index。
  • 我们维护一个变量 right_max,用来记录从右边到当前位置为止遇到的最高墙体。
  • 对于这个区间的任何一根柱子 height[i],它左边的最高墙一定是全局最高点 max_height。
  • 因此,在该位置 i 的蓄水高度瓶颈,就完全取决于其右边的最高墙 right_max。
  • 我们在遍历过程中,不断更新 right_max 并累加每个位置的蓄水量 right_max - height[i]。

将第二步和第三步计算出的蓄水量相加,就得到了最终的总量。

复杂度分析

  • 时间复杂度: O(n)。
  • 空间复杂度: O(1)。
class Solution {
public:int trap(vector<int>& height) {int n = height.size();if (n < 3) {return 0;}// 1. 找到全局最高点int max_height = 0;int max_index = 0;for (int i = 0; i < n; ++i) {if (height[i] > max_height) {max_height = height[i];max_index = i;}}int total_water = 0;// 2. 处理左半部分 (从 0 到 max_index)int left_max = 0;for (int i = 0; i < max_index; ++i) {// 更新左侧遇到的最高墙left_max = max(left_max, height[i]);// 计算当前位置的蓄水量并累加total_water += left_max - height[i];}// 3. 处理右半部分 (从 n-1 到 max_index)int right_max = 0;for (int i = n - 1; i > max_index; --i) {right_max = max(right_max, height[i]);total_water += right_max - height[i];}return total_water;}
};

http://www.dtcms.com/a/271904.html

相关文章:

  • 开源 Arkts 鸿蒙应用 开发(六)数据持久--文件和首选项存储
  • [特殊字符] LLM(大型语言模型):智能时代的语言引擎与通用推理基座
  • 藏文识别技术的关键挑战与解决方案
  • 使用Visual Studio Code附加到Jetty进程调试Servlet
  • 小皮面板搭建pikachu靶场
  • 710 Mybatis实战
  • Go语言中map的零值是什么?对零值map进行读写操作会发生什么?如何正确初始化和使用map?
  • 力扣-73.矩阵置零
  • 【会员专享数据】2013-2024年我国省市县三级逐年SO₂数值数据(Shp/Excel格式)
  • 2025年材料应用与计算机科学国际会议(MACS 2025)
  • C++中的左值、右值与std::move()
  • 数据 + 模型 驱动 AI Native 应用发展
  • 利用DBeaver实现异构数据库数据定时任务同步
  • 计算机网络实验——以太网安全实验
  • Flutter 知识点总结
  • React虚拟DOM的进化之路
  • Vue.js 过渡 动画
  • 如何在Flutter开发中系统性减少知识盲区
  • 使用 FreeRTOS 实现简单多任务调度(初识 RTOS)
  • Excalidraw:一款轻量、高效、极具手感的在线白板工具
  • 【免费数据】2020年中国高精度耕地范围矢量数据
  • 解析几何几百年重大错误:将无穷多各异圆盘(球)误为同一点集
  • 语音转文字「本地化」新解!Whisper Web+cpolar实现零服务器部署与远程操作
  • 大数据在UI前端的应用创新:基于用户画像的精准广告投放系统
  • imx6ull-裸机学习实验17——SPI 实验
  • 《数据库》第一次作业:MySQL数据库账户及授权
  • FeatherScan v4.0 – 适用于Linux的全自动内网信息收集工具
  • 2025.07.09华为机考真题解析-第二题200分
  • 华为L1-L6流程体系核心框架
  • 2025.07.09华为机考真题解析-第三题300分