1. 题意

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

2. 题解

这个题不会做,全部是看得题解捏。

不过能看懂题解感觉自己也很棒了!

看完题解后感觉最难的是如何求出有多少积水,

答案直接给的是当前位置的积水量是它左右两边两个最大值的最小值

减去当前的高度。

灵茶山艾府的题解中解释了为什么?如果比两端最高的高积水肯定会

从一侧流出去。

不过我肯定是想不到的。

有了这个结论之后,其实就可以做一下了。

对于每个高度,求出它左右两侧的最高高度,从而计算积水的高度。

2.1 前后缀最大值

算出前后缀的最高高度。

再遍历一次计算积水量。

class Solution {
public:int trap(vector<int>& height) {int n = height.size();int ans = 0;vector<int> left_max(n, 0);vector<int> right_max(n, 0);for (int i = 1; i < n; ++i) {left_max[i] = max(left_max[i - 1], height[i - 1]);}for (int i = n - 2; ~i; --i) {right_max[i] = max(right_max[i + 1], height[i + 1]);}for (int i = 1; i < n - 1; ++i) {ans += max(0, min(left_max[i], right_max[i]) - height[i] );}return ans;}
};

当然可以稍微优化下空间,不过差别不大。

class Solution {
public:int trap(vector<int>& height) {int n = height.size();int ans = 0;vector<int> rightMx(n + 1, -1);for (int i = n - 1; ~i; --i) {rightMx[i] = max(height[i], rightMx[i + 1]);}int leftMx = height[0];for (int i = 1; i < n - 1; ++i) {ans += max( 0, min(leftMx, rightMx[i + 1]) - height[i]);leftMx = max(height[i], leftMx);}return ans;}
};

时间复杂度O(n)O(n)O(n)

空间复杂度O(n)O(n)O(n)

2.2 双指针

双指针的解法确实比较巧妙,先说下做法,再证明为什么正确,最后给出代码。

思路是,左指针lll指向左端,右指针rrr指向右端,同时维护两个变量:

leftMaxleftMaxleftMax表示lll左端的最大值,

rightMaxrightMaxrightMax表示rrr右端的最大值。

leftMax<rightMaxleftMax <rightMaxleftMax<rightMax时,

左指针元素积水高度为leftMax−height[l]leftMax -height[l]leftMaxheight[l],左指针右移。

leftMax≥rightMaxleftMax \ge rightMaxleftMaxrightMax时,

右指针元素积水高度为rightMax−height[r]rightMax - height[r]rightMaxheight[r],右指针左移。

为什么这个策略正确呢? 下面简要证明。

我们令lmx(i)=max⁡{height[k],0≤k≤i}lmx(i) = \max\{height[k] , 0 \le k \le i\}lmx(i)=max{height[k],0ki}

也就是lmx(i)lmx(i)lmx(i)表示下标iii左端的最大值。

我们令rmx(i)=max⁡{height[k],i≤k≤n−1}rmx(i)=\max\{ height[k],i \le k \le n-1\}rmx(i)=max{height[k],ikn1},

也就是rmx(i)rmx(i)rmx(i)表示下标iii右端的最大值。

我们令w[i]w[i]w[i]表示位置iii的积水高度,

那么w[i]=min⁡(lmx(i),rmx(i))−height[i]w[i] =\min(lmx(i),rmx(i))-height[i]w[i]=min(lmx(i),rmx(i))height[i]

l≤rl \le rlr时,我们很容易得到

lmx(l)≤lmx(r)rmx(l)≥rmx(r)lmx(l) \le lmx(r)\\ rmx(l) \ge rmx(r) lmx(l)lmx(r)rmx(l)rmx(r)

上面的leftMaxleftMaxleftMax相当于lmx(l)lmx(l)lmx(l)rightMaxrightMaxrightMax相当于rmx(r)rmx(r)rmx(r)

lmx(l)<rmx(r)lmx(l) < rmx(r)lmx(l)<rmx(r)时,必然有lmx(l)<rmx(r)≤rmx(l)lmx(l) < rmx(r) \le rmx(l)lmx(l)<rmx(r)rmx(l)

因此min⁡(lmx(l),rmx(l))=lmx(l)\min(lmx(l), rmx(l))=lmx(l)min(lmx(l),rmx(l))=lmx(l)

同理lmx(l)≥rmx(r)lmx(l) \ge rmx(r)lmx(l)rmx(r)时,必然有rmx(r)≤lmx(l)≤lmx(r)rmx(r) \le lmx(l) \le lmx(r)rmx(r)lmx(l)lmx(r)

因此min⁡(lmx(r),rmx(r))=rmx(r)\min( lmx(r), rmx(r)) = rmx(r)min(lmx(r),rmx(r))=rmx(r)

综上,这个策略是正确的。

下面给出不那么重要的代码:

class Solution {
public:int trap(vector<int>& height) {int n = height.size();int ans = 0;int l = 0;int r = n - 1;int leftMax = 0;int rightMax = 0;while (l <= r) {leftMax = max( leftMax, height[l]);rightMax = max( rightMax, height[r]);if ( height[l] < height[r]) {ans += leftMax - height[l];++l;}else {ans += rightMax - height[r];--r;}}return ans;}
};

时间复杂度O(n)O(n)O(n)

空间复杂度O(1)O(1)O(1)

2.3 单调栈

说实话不是很明白单调栈的做法怎么想到的,

它比前后缀的做法要难理解的多,也不知道怎么来的,纯纯为了

用一下这个数据结构?

核心思想是逐步填充积水高度。

当栈顶元素大于新元素时,直接将新元素入栈。

否则,将栈顶元素出栈,令为kkk

将积水高度填充到与新栈顶leftleftleft或新元素height[i]height[i]height[i]的最小值,

min⁡(height[i],height[left])−height[k]\min(height[i], height[left]) -height[k]min(height[i],height[left])height[k]

填充的宽度就是i−left−1i-left-1ileft1

不断重复这个过程直到栈空或者栈顶大于新元素,再将新元素入栈。

叙述起来很复杂,还是看下图吧,像个一步步填坑的过程。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

下面给出代码

class Solution {
public:int trap(vector<int>& height) {int n = height.size();stack<int> s;int ans = 0;for (int i = 0; i < n; ++i) { while (!s.empty() && height[s.top()] <= height[i]) {int k = s.top();s.pop();if (!s.empty()) {int left = s.top();ans += ( i - left - 1 ) * (min(height[i], height[left]) - height[k] );}}s.push( i );}return ans;}
};

时间复杂度O(n)O(n)O(n),空间复杂度O(n)O(n)O(n)

3. 参考

leetcode
0x3f

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/95829.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/95829.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/95829.shtml

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

相关文章

Spring Boot 整合 Thymeleaf 模板引擎:从零开始的完整指南

引言&#xff1a;为什么选择 Thymeleaf&#xff1f; Thymeleaf 是一个现代化的服务器端 Java 模板引擎&#xff0c;专为 Web 开发而设计。与 JSP 不同&#xff0c;Thymeleaf 模板是纯 HTML 文件&#xff0c;可以直接在浏览器中预览&#xff0c;无需后端服务器支持。这种"…

pytest介绍(python测试框架)(@pytest.mark.parametrize、@pytest.fixtures)

文章目录**1. 核心特点**- **简洁易用**&#xff1a;无需复杂的配置&#xff0c;只需编写简单的函数或类即可进行测试。- **丰富的断言**&#xff1a;直接使用 Python 内置的 assert 语句&#xff0c;失败时提供详细的错误信息。- **自动发现测试**&#xff1a;通过约定的命名规…

[Python 基础课程]继承

在 Python 的面向对象编程&#xff08;OOP&#xff09;中&#xff0c;继承&#xff08;Inheritance&#xff09; 是一种重要的机制&#xff0c;它允许一个类&#xff08;称为子类或派生类&#xff09;从另一个类&#xff08;称为父类、基类或超类&#xff09;中继承属性和方法。…

QT之设计器组件功能(8大类55个组件)

组件名称 功能描述关键属性1. Layouts&#xff08;布局组件&#xff09;(1) Vertical Layout&#xff08;垂直布局&#xff09;将子控件按垂直方向依次排列layoutSpacing&#xff1a;控件之间的间距layoutMargin&#xff1a;布局边缘的边距layoutStretch&#xff1a;设置各控件…

java中list的api详细使用

在Java中&#xff0c;List是集合框架中最常用的接口之一&#xff0c;继承自Collection&#xff0c;代表有序、可重复的元素集合&#xff08;允许null元素&#xff09;。其核心实现类有ArrayList&#xff08;数组实现&#xff0c;随机访问高效&#xff09;、LinkedList&#xff…

Azure AI Search 探索总结

Azure AI Search 原名 Azure Cognitive Service&#xff0c;是Azure中用来给AI项目构建知识库的组件。知识库本质和数据库很像&#xff0c;但是内部的存储结构和检索算法不一样。比如并不是知识库的每一列都可以用来过滤、检索或group by&#xff0c;而是要根据实际情况配置。A…

高效解决 pip install 报错 SSLError: EOF occurred in violation of protocol

高效解决 pip install 报错 SSLError: EOF occurred in violation of protocol 标签&#xff1a; Python, pip, SSLError, Clash, 网络代理, 问题解决 一、问题描述 在Python开发中&#xff0c;pip 是我们最亲密的伙伴。然而&#xff0c;当你身处需要科学上网的环境&#xff0c…

CSS 核心知识点全解析:从基础到实战应用

大家好&#xff01;今天这篇文章将系统总结 CSS 的核心知识点&#xff0c;从最基础的样式引入到复杂的选择器应用&#xff0c;再到盒子模型、文本处理等实战技巧&#xff0c;全程结合代码示例&#xff0c;让你轻松掌握 CSS 的精髓。一、CSS 是什么&#xff1f;为什么需要它&…

ClickHouse的学习与了解

什么是ClickHouse&#xff1f; ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。 在传统的行式数据库系统中&#xff0c;数据按如下顺序存储&#xff1a;RowWatchIDJavaEnableTitleGoodEventEventTime#0893543506621Investor Relations12016/5/18 5:19#1903295…

安卓11 12系统修改定制化_____修改系统 解锁system分区 去除data加密 自由删减系统应用

在定制化系统中。修改系统分区 解锁system。让用户可以自由删减应用。这个在定制化服务中比较常见。对于此项修改服务。需要我们了解基础的分区常识以及常用的几种基础修改步骤。 通过博文了解💝💝💝 1💝💝💝-----修改rom 解锁 system 分区有什么意义 2💝💝…

JetPack系列教程(八):PDF库——让Android应用也能优雅“翻页”

JetPack系列教程&#xff08;八&#xff09;&#xff1a;PDF库——让Android应用也能优雅“翻页” 在Android开发的世界里&#xff0c;加载PDF文件一直是个让人又爱又恨的“小妖精”。爱它&#xff0c;因为PDF是文档界的“万能钥匙”&#xff1b;恨它&#xff0c;因为原生Andr…

Three.js三大组件:场景(Scene)、相机(Camera)、渲染器(Renderer)

上一篇中我们学习了第一个Three.js场景"Hello World"。这一篇就来学习three.js的核心组件。 此图来源&#xff08;Three.js中文网&#xff09; three.js的核心由三大组件构成&#xff1a;场景(Scene)、相机(Camera)和渲染器(Renderer)。下面我将详细介绍这三大件的作…

AI幻觉终结之后:GPT-5开启的“可靠性”新赛道与开发者生存指南

摘要&#xff1a; Sam Altman关于GPT-5将基本终结幻觉的宣告&#xff0c;不仅仅是一次技术升级&#xff0c;它标志着一个“万物皆可AI&#xff0c;但万事皆需验证”的混乱时代的结束。本文将从一个全新的战略视角出发&#xff0c;探讨当“可靠性”取代“创造性”成为AI竞赛的核…

ubuntu远程桌面很卡怎么解决?

服务端方案 完成XRDP的性能优化配置&#xff1a; 1. 首先检查当前的xrdp.ini文件 grep -n "tcp_send_buffer_bytes" /etc/xrdp/xrdp.ini2. 编辑xrdp.ini文件&#xff0c;修改TCP发送缓冲区大小 sudo sed -i s/#tcp_send_buffer_bytes32768/tcp_send_buffer_bytes4194…

[Linux] Linux系统负载监控 Linux服务管理

目录 Linux系统负载监控 系统负载介绍 查看系统负载 负载解读 top 命令 Linux服务管理 systemd 介绍 系统启动管理进程 基本概念 systemd 架构 unit 类型 查看 unit 列表信息 查看单个 unit 信息 控制系统服务 systemctl 命令 unit 配置文件 例&#xff1a;开发…

vector 手动实现 及遇到的各种细节问题

之前对vector的一些功能使用了一下 接下来手动实现一下vector vector的实现和string还是有不小区别的 有很多地方都有细节的问题不同于string的成员变量一个指针一个size一个capacity的成员变量 vector里面存的是三个迭代器iterator 这的迭代器其实就是模版T的指针 这样就…

OpenStack Neutron中的L2 Agent与L3 Agent:新手友好指南

引言&#xff1a;云网络的幕后英雄 在当今的云计算世界中&#xff0c;OpenStack作为开源云平台的佼佼者&#xff0c;为成千上万的企业提供了灵活、可扩展的基础设施服务。而在OpenStack的众多组件中&#xff0c;Neutron&#xff08;网络服务&#xff09;扮演着至关重要的角色—…

【自用】JavaSE--特殊文件Properties与XML、日志技术

特殊文件概述使用特殊文件可以存储多个有关系的数据&#xff0c;作为系统的配置信息属性文件类似于键值对&#xff0c;一一对应存储数据(比如用户名与密码)XML文件存储多个用户的多个属性更适合&#xff0c;适合存储更复杂的数据Properties注&#xff1a;这个属性文件的后缀即使…

中本聪思想与Web3的困境:从理论到现实的跨越

一、中本聪思想的核心精髓中本聪通过比特币白皮书提出的核心思想&#xff0c;可归纳为三大支柱&#xff1a;去中心化货币体系目标&#xff1a;摆脱中央机构控制&#xff0c;避免通货膨胀和政治干预&#xff08;如2008年金融危机暴露的中心化风险&#xff09;。实现路径&#xf…

Centos 用户管理

一.创建用户 在 root账户 或 sudo 权限下 1. 创建用户 useradd xiaoyangzi2.为该用户设置密码或修改密码 passwd xiaoyangzi3. 将用户加入wheel用户组 在 CentOS 中&#xff0c;属于 wheel 组的用户默认可以使用 sudo 权限。 查看所属用户组: groups xiaoyangzi将 xiaoyangzi 加…