题目链接: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.pswp.cn/pingmian/88773.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/88773.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/88773.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

宝塔配置pgsql可以远程访问

本地navicat premium 17.0 可以远程访问pgsql v16.1宝塔的软件商店里&#xff0c;找到pgsql管理器&#xff1b;在pgsql管理器里找到客户端认证&#xff1a;第二步&#xff1a;配置修改&#xff0c;CtrlF 查找listen_addresses关键字&#xff1b;第三步&#xff1a;在navicat里配…

小架构step系列12:单元测试

1 概述 测试的种类很多&#xff1a;单元测试、集成测试、系统测试等&#xff0c;程序员写代码进行测试的可以称为白盒测试&#xff0c;单元测试和集成测试都可以进行白盒测试&#xff0c;可以理解为单元测试是对某个类的某个方法进行测试&#xff0c;集成测试则是测试一连串的…

SpringBoot3-Flowable7初体验

目录简介准备JDKMySQLflowable-ui创建流程图要注意的地方编码依赖和配置控制器实体Flowable任务处理类验证启动程序调用接口本文源码参考简介 Flowable是一个轻量的Java业务流程引擎&#xff0c;用于实现业务流程的管理和自动化。相较于老牌的Activiti做了一些改进和扩展&…

phpMyAdmin:一款经典的MySQL在线管理工具又回来了

phpMyAdmin 是一个免费开源、基于 Web 的 MySQL/MariaDB 数据库管理和开发工具。它提供了一个直观的图形用户界面&#xff0c;使得我们无需精通复杂的 SQL 命令也能执行大多数数据库管理任务。 phpMyAdmin 项目曾经暂停将近两年&#xff0c;不过 2025 年又开始发布新版本了。 …

存储服务一NFS文件存储概述

前言&#xff1a; 网络文件系统&#xff08;Network File System&#xff0c;NFS&#xff09;诞生于1984年&#xff0c;由Sun Microsystems首创&#xff0c;旨在解决异构系统间的文件共享需求。作为一种基于客户端-服务器架构的分布式文件协议&#xff0c;NFS允许远程主机通过T…

libimagequant 在 mac 平台编译双架构

在 macOS 上编译 libimagequant 的双架构&#xff08;aarch64 x86_64&#xff09;通用二进制库&#xff0c;以下是完整步骤&#xff1a;​​1. 准备 Rust 工具链​​ # 安装两个目标平台 rustup target add aarch64-apple-darwin x86_64-apple-darwin# 确认安装成功 rustup ta…

暑期自学嵌入式——Day01(C语言阶段)

点关注不迷路哟。你的点赞、收藏&#xff0c;一键三连&#xff0c;是我持续更新的动力哟&#xff01;&#xff01;&#xff01; 主页&#xff1a; 一位搞嵌入式的 genius-CSDN博客https://blog.csdn.net/m0_73589512?spm1011.2682.3001.5343感悟&#xff1a; 今天我认为最重…

Flutter基础(前端教程⑧-数据模型)

这个示例展示了如何创建数据模型、解析 JSON 数据&#xff0c;以及在 UI 中使用这些数据&#xff1a;import package:flutter/material.dart; import dart:convert;void main() {// 示例&#xff1a;手动创建User对象final user User(id: 1,name: 张三,age: 25,email: zhangsa…

SSRF10 各种限制绕过之30x跳转绕过协议限制

ssrf漏洞在厂商的处理下可能进行一些特殊处理导致我们无法直接利用漏洞 有以下四种&#xff1a; 1.ip地址限制绕过 2.域名限制绕过 3.30x跳转绕过域名限制 4.DNS rebinding绕过内网ip限制 本章我们讲30x跳转绕过域名限制 30x跳转绕过域名限制 之前我们使用ssrf漏洞时可以…

DNS解析过程和nmap端口扫描

目录 DNS解析流程&#xff1a; nmap端口扫描 指定扫描方式 TCP全连接扫描 -sT SYN半连接扫描 -sS -sT和 -sS的区别 Linux提权 利用好谷歌语法查找敏感信息 如果自己搭建了网站文件要放在phpstudy_pro\WWW下。 如果想要使用域名访问网站&#xff0c;需要在phpstudy_pro…

【基于开源大模型(如deepseek)开发应用及其发展趋势的一点思考】

1. 开源大模型技术发展现状1.1 DeepSeek等主流开源大模型的技术特性分析 DeepSeek作为当前最具代表性的开源大模型之一&#xff0c;其技术架构具有多项创新特性。该模型采用混合专家架构(MoE)&#xff0c;通过将视觉编码分离为"理解"和"生成"两条路径&…

java8 ConcurrentHashMap 桶级别锁实现机制

Java 8 ConcurrentHashMap 桶级别锁实现机制 Java 8 中的 ConcurrentHashMap 抛弃了分段锁设计&#xff0c;采用了更细粒度的桶级别锁&#xff08;bucket-level locking&#xff09;实现&#xff0c;这是其并发性能提升的关键。下面详细解析其实现原理&#xff1a; 1. 基本实现…

Python正则表达式实战指南

一 正则表达式库正则表达式是文本处理中不可或缺的强大工具&#xff0c;Python通过re模块提供了完整的正则表达式支持。本文将详细介绍re模块中最常用的match()、search()和findall()函数&#xff0c;以及贪婪模式与非贪婪模式的区别&#xff0c;帮助读者掌握Python中正则表达式…

使用球体模型模拟相机成像:地面与天空的可见性判断与纹理映射

在传统相机模拟中&#xff0c;地面通常被建模为一个平面&#xff08;Plane&#xff09;&#xff0c;这在低空场景下是合理的。但在更大视场范围或远距观察时&#xff0c;地球的曲率不可忽视。因此&#xff0c;我们需要将地面模型从平面升级为球体&#xff0c;并基于球面与光线的…

Agent自动化与代码智能

核心问题&#xff1a; 现在很多团队做AI系统有个大毛病&#xff1a;只顾追求“高大上”的新技术&#xff08;尤其是AI Agent&#xff09;&#xff0c;不管实际业务需不需要。 结果系统搞得又贵、又复杂、还容易出错。大家被“Agent”这个概念搞晕了&#xff1a;到底啥时候用简单…

SQL141 试卷完成数同比2020年的增长率及排名变化

SQL141 试卷完成数同比2020年的增长率及排名变化 withtemp as (selectexam_id,tag,date(submit_time) as submit_timefromexamination_infoleft join exam_record using (exam_id)wheresubmit_time is not null),2021_temp as (selecttag,count(*) as exam_cnt_21,rank() over…

C语言<数据结构-单链表>

链表是一种常见且重要的数据结构&#xff0c;在 C 语言中&#xff0c;它通过指针将一系列的节点连接起来&#xff0c;每个节点可以存储不同类型的数据。相比数组&#xff0c;链表在插入和删除元素时不需要移动大量数据&#xff0c;具有更好的灵活性&#xff0c;尤其适合处理动态…

archive/tar: unknown file mode ?rwxr-xr-x

这个是我在docker build报错的&#xff0c;这是一个node.js项目。我猜你也是一个node.js下的项目&#xff0c;或者前端项目。 解决方法&#xff1a; .dockerignore里面写一下node_modules就行了。 未能解决&#xff1a;archive/tar&#xff1a;未知文件模式&#xff1f;rwxr-…

【前端】ikun-markdown: 纯js实现markdown到富文本html的转换库

文章目录背景界面当前支持的 Markdown 语法不支持的Markdown 语法代码节选背景 出于兴趣,我使用js实现了一个 markdown语法 -> ast语法树 -> html富文本的库, 其速度应当慢于正则实现的同类js库, 但是语法扩展性更好, 嵌套列表处理起来更方便. 界面 基于此js实现vue组…

【echarts踩坑记录】为什么第二个Y轴最大值不整洁

目录问题复现示意图&#xff1a;解决方法有以下几种&#xff1a;1. 在y轴配置中手动设置max属性&#xff1a;2. 使用ECharts提供的坐标轴标签格式化功能&#xff1a;&#x1f680;写在最后问题复现示意图&#xff1a; 今天在用echarts图表的时候&#xff0c;出现了一个小问题。…