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

leetcode_42 接雨水

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.lryc.cn/news/623028.html

相关文章:

  • H20芯片与中国的科技自立:一场隐形的博弈
  • 内网穿透实战笔记 1panel 面板部署 frps,Windows 部署 frpc
  • Win11和Win10共享打印机提示709用添加Windows凭据来解决的小方法
  • 自适应阈值二值化参数详解 ,计算机视觉,图片处理 邻域大小 调整常数(C=3)和可视化调节参数的应用程序
  • vscode中用python调用matlab的函数(环境安装)
  • 计算机网络:(十五)TCP拥塞控制与拥塞控制算法深度剖析
  • 安全审计-firewall防火墙
  • 在STM32F103上进行FreeRTOS移植和配置(STM32CubeIDE)
  • MySQL的《Buffer-pool》和《连接池》介绍
  • LangChain4j:基于 SSE 与 Flux 的 AI 流式对话实现方案
  • lesson40:PyMySQL完全指南:从基础到高级的Python MySQL交互
  • 数据结构:层序遍历 (Level-order Traversal)
  • 图论Day4学习心得
  • Kafka 面试题及详细答案100道(11-22)-- 核心机制1
  • 代码随想录Day52:图论(孤岛的总面积、沉没孤岛、水流问题、建造最大岛屿)
  • Cmake学习笔记
  • 代码随想录算法训练营四十三天|图论part01
  • 数字化与人工智能的崛起及其社会影响研究报告
  • 基于uni-app+vue3实现的微信小程序地图范围限制与单点标记功能实现指南
  • Altium Designer 22使用笔记(7)---网表导入,叠层设置
  • 【电路笔记 通信】AXI4-Lite协议 论文阅读 简化的高级可扩展接口(AdvancedeXtensibleInterface4Lite)
  • 【计算机网络架构】混合型架构简介
  • 车载诊断架构 --- 怎么解决对已量产ECU增加具体DTC的快照信息?
  • 超越Transformer:大模型架构创新的深度探索
  • 【自动化运维神器Ansible】Ansible逻辑运算符详解:构建复杂条件判断的核心工具
  • 11、软件需求工程
  • 【系统分析师】软件需求工程——第11章学习笔记(下)
  • 架构调整决策
  • 软件需求管理过程详解
  • M-LAG双活网关