代码随想录算法训练营第60期第五十二天打卡

article/2025/7/22 7:07:49

        大家好,昨天我们重点讲解了单调栈的问题,我们今天的题目还是继续围绕单调栈展开,我们上节课其实对单调栈已经有了大致的了解,今天的第一道题目大家务必要注意很重要,接雨水问题我们会涉及到单调栈与双指针,这道题目可以说是面试中的高频题目,好了那我们就走进今天的题目。

第一题对应力扣编号为42的题目接雨水

        这道题目我们以前没有接触过类似的题目,那我们就直接看题目要求:

       看完题目我明白了这道题目的含义,我们是要求我们能接多少雨水,大家看上面这幅示意图,其实两根柱子之间首先不能相邻那么其实可以接的雨水数量是那根短的柱子的长度,其实就是短板效应,但是重点还得考虑中间的这种情况,这种情况不太容易描述,如果有两个相邻的柱子不相邻的话其实可以分开看,其实中间部分有一个高度为1的柱子,它们的周围就有高度为2和3的柱子而且中间还隔了一个高度为0的柱子,大家就暂时这样理解,想接到雨水两边就都要有柱子来挡着防止流走,那我们就看看这道题目的解题思路:

     首先我们还是先来看暴力的一种解法,其实可以分为两种思路一种是按行求另一种是按列求,大家看示意图:

        那这里我们使用按列求,因为似乎按列求比较容易理解,我们是可以确定宽度都是1的,然后我们也会慢慢发现规律,我们每一列可以接的雨水单位数量就是其左侧最高的柱子高度与右侧最高的柱子高度的高度差再减去当前列的柱子高度就可以了,比如说我们求列4的雨水高度,列4 左侧最高的柱子是列3,高度为2(以下用lHeight表示)。列4 右侧最高的柱子是列7,高度为3(以下用rHeight表示)。列4 柱子的高度为1(以下用height表示),那么列4的雨水高度为 列3和列7的高度最小值减列4高度,即: min(lHeight, rHeight) - height。其实这样就可以了,列4的雨水高度求出来了,宽度为1,相乘就是列4的雨水体积了。那么接下来我们就可以遍历所有的列,注意我们的第一列与最后一列是一定不可以接到雨水的,根据规律我们就可以给出解题代码:

class Solution {
public:int trap(vector<int>& height) {//保存最后的雨水的单位数int sum = 0;for (int i = 0; i < height.size(); ++i){//注意我们第一列与最后一列是不接雨水的if (i == 0 || i == height.size() - 1) continue;int rheight = height[i];//存储右边最高的柱子int lheight = height[i];//存储左边最高的柱子//找到右边最高的柱子for (int r = i + 1; r < height.size(); ++r){if (height[r] > rheight) rheight = height[r];}//找到左边最高的柱子for (int l = i - 1; l >= 0; --l){if (height[l] > lheight) lheight = height[l];}int h = min(lheight, rheight) - height[i];if (h > 0) sum += h;}return sum;}
};

         这样虽然是超时的,但是我希望大家可以理解这种思路,我们接下来就看不超时的例子,其实我们是可以考虑使用双指针进行优化的,我们为了找到左边的柱子最高值与右边柱子的最高值我们需要遍历两次,为了得到两边的最高高度,使用了双指针来遍历,每到一个柱子都向两边遍历一遍,这其实是有重复计算的。我们把每一个位置的左边最高高度记录在一个数组上(maxLeft),右边最高高度记录在一个数组上(maxRight),这样就避免了重复计算。我们来看一下我们的双指针思路:

class Solution {
public:int trap(vector<int>& height) {if (height.size() <= 2) return 0;//定义两个数组来分别存储每一个柱子的左侧的柱子高度最大值与右侧的柱子高度的最大值vector<int> maxLeft(height.size(), 0);vector<int> maxRight(height.size(), 0);int size = maxRight.size();//记录左边柱子的最大高度maxLeft[0] = height[0];for (int i = 1; i < height.size(); ++i){maxLeft[i] = max(height[i], maxLeft[i - 1]);}//记录右边柱子的最大高度(注意我们这里要倒序遍历)maxRight[size - 1] = height[size - 1];for (int i = size - 2; i >= 0; --i){maxRight[i] = max(height[i], maxRight[i + 1]);}//求和int sum = 0;for (int i = 0; i < size; ++i){int count = min(maxLeft[i], maxRight[i]) - height[i];if (count > 0) sum += count;}return sum;}
};

       这种解法是可以通过本题的,大家要理解,当然我们今天的主题是单调栈,我们还是要学单调栈如何解决这个问题:首先单调栈是按照行方向来计算雨水,我们上面已经给出过按行方向的示意图,还得考虑使用单调栈内元素的顺序,其实也很明显就是使用从小到大的顺序,因为一旦发现添加的柱子高度大于栈头元素了,此时就出现凹槽了,栈头元素就是凹槽底部的柱子,栈头第二个元素就是凹槽左边的柱子,而添加的元素就是凹槽右边的柱子。如果遇到相同的元素,更新栈内下标,就是将栈里元素(旧下标)弹出,将新元素(新下标)加入栈中。因为我们要求宽度的时候 如果遇到相同高度的柱子,需要使用最右边的柱子来计算宽度。还有一个问题我们需要理解,我们其实不需要pair来存储每根柱子的高度与下标,我们其实直接存放下标就可以,我们通过下标就可以访问到对应的高度,这样我们就可以尝试给出本思路的解题代码:

class Solution {
public:int trap(vector<int>& height) {if (height.size() <= 2) return 0;stack<int> st; //存着下标,计算的时候用下标对应的柱子高度st.push(0);//存储最终的和int sum = 0;for (int i = 1; i < height.size(); ++i){if (height[i] < height[st.top()]){st.push(i);} else if (height[i] == height[st.top()]){st.pop();st.push(i);}else{while(!st.empty() && height[i] > height[st.top()]){int mid = st.top();st.pop();if (!st.empty()){int h = min(height[i], height[st.top()]) - height[mid];int w = i - st.top() - 1; //注意减1求中间宽度sum += h * w;}}st.push(i);}}return sum;}
};

    这种思路比较难,大家注意理解。

第二题对应力扣题号为84的题目柱状图中最大的矩形

        这道题目其实如果以前没有接触过单调栈的话就很难了,但是如果我们接触过单调栈的话或许就不难了,我们还是先看一下题目的具体要求:

       这道题目其实与上一道题目有些类似,其实那里要收集雨水的话是有条件的,但在这里其实凑出最大的矩形也是有条件的,其实还有点难,因为我的柱子可以高但如果不够宽的话会不会就比虽说柱子不够高但是够宽的小了,这点大家是不是需要留意一下?我们就看看这道题目我们如何使用单调栈来解决?其实还是可以使用暴力解法的,我可以直接给大家解题代码我就不解释了,因为暴力解法是超时的,大家见下面的解题代码:

class Solution {
public:int largestRectangleArea(vector<int>& heights) {//表示的是矩形的面积int sum = 0;for (int i = 0; i < heights.size(); ++i){int left = i;int right = i;for (; left >= 0; --left){if (heights[left] < heights[i]) break;}for (; right < heights.size(); ++right){if (heights[right] < heights[i]) break;}int w = right - left - 1;int h = heights[i];sum = max(sum, w * h);}return sum;}
};

        接下来我们重点看单调栈的做法:因为本题是要找每个柱子左右两边第一个小于该柱子的柱子,所以从栈头(元素从栈头弹出)到栈底的顺序应该是从大到小的顺序!其实其他的地方与上面的一题接雨水都一样了,其实接雨水那道题目我们是不是求右边第一个比它大的元素,而这道题目我们是不是应该是求右边第一个比它小的元素,注意我们以每一个柱子的高度为基准的话什么时候可以凑出矩形,有些情况我们是不可以往左或者往右延展的,比如说题目示例里面我们5是不是无法向左边延展,因为向左根本就凑不出矩形。

       本题目还有一件很重要的事情,我如果要去求比如说右边的比自身小的第一个元素,那么我当前给出的数组如果就是一个单调递增的数组,那我还有收获结果的过程吗?其实就没有了,因为我什么时候可以收获结果,很明显是当前元素的值小于我栈顶元素的时候才可以,因为我们就会考虑在结尾处加一个0,这样我们才可以收获结果,同理如果我的数组是一个单调递减的数组,那其实我就需要在开头加一个0,如果数组本身是降序的,例如 [8,6,4,2],在 8 入栈后,6 开始与8 进行比较,此时我们得到 mid(8),right(6),但是得不到 left。为了避免出现空栈就存在这样了这样的操作,接下来我们就尝试给出我们的解题代码:

class Solution {
public:int largestRectangleArea(vector<int>& heights) {int result = 0;stack<int> st;heights.insert(heights.begin(), 0); // 数组头部加入元素0heights.push_back(0); // 数组尾部加入元素0st.push(0);// 第一个元素已经入栈,从下标1开始for (int i = 1; i < heights.size(); i++) {if (heights[i] > heights[st.top()]) { // 情况一st.push(i);} else if (heights[i] == heights[st.top()]) { // 情况二st.pop(); // 这个可以加,可以不加,效果一样,思路不同st.push(i);} else { // 情况三while (!st.empty() && heights[i] < heights[st.top()]) { // 注意是whileint mid = st.top();st.pop();if (!st.empty()) {int left = st.top();int right = i;int w = right - left - 1;int h = heights[mid];result = max(result, w * h);}}st.push(i);}}return result;}
};

今日总结

       今天的单调栈其实不简单,有时候我们会分不清什么时候使用递增的单调栈,什么时候使用递减的单调栈,这个其实取决于我们的题目要求,如果我们是需要求比如右边第一个大于本身的数我们就需要使用单调递增的单调栈,如果我们要求右边第一个小于本身的数我们就需要使用单调递减的单调栈,还有一个问题我们什么时候是收获结果的时候,希望大家可以理解,我们今天的分享就到这里了,明天我们就会开始图论,大家明天见!


http://www.hkcw.cn/article/YImySJaquA.shtml

相关文章

新能源集群划分+电压调节!基于分布式能源集群划分的电压调节策略!

适用平台&#xff1a;MatlabYalmip Cplex (具体操作已在程序文件中说明) 参考文献&#xff1a;基于分布式能源集群化分的电压调节策略[D]. 一、文献解读 1. 主要内容/创新点 提出了一种基于分布式能源集群化的电压调节策略&#xff0c;计及分布式能源的有功、无功调节能力&a…

【C++】22. 红黑树封装实现Mymap和Myset

上一章节我们实现了红黑树&#xff0c;这一章节我们就用红黑树封装来实现一个我们自己的map和set 1. 源码及框架分析 SGI-STL 3.0版本的源代码中&#xff0c;map和set的实现主要分布在若干头文件中&#xff0c;这些头文件构成了这两个容器的完整实现架构&#xff1a; 核心头文…

论文速读《UAV-Flow Colosseo: 自然语言控制无人机系统》

论文链接&#xff1a;https://arxiv.org/abs/2505.15725项目主页&#xff1a;https://prince687028.github.io/UAV-Flow/ 0. 简介 近年来&#xff0c;无人机技术蓬勃发展&#xff0c;但如何让无人机像智能助手一样理解并执行人类语言指令&#xff0c;仍是一个前沿挑战。现有研…

关于表连接

目录 1.左连接 2.右连接 3.内连接 4.全外连接 5.笛卡尔积 -- 创建表A CREATE TABLE A(PNO VARCHAR2(10) PRIMARY KEY, PAMT NUMBER, A_DATE DATE);-- 向表A插入数据 INSERT INTO A VALUES (01001, 100, TO_DATE(2005-01-01, YYYY-MM-DD)); INSERT INTO A VALUES (010…

【面试 - 遇到的问题 - 优化 - 地图】腾讯地图轨迹回放 - 回放的轨迹时间要和现实时间对应(非匀速)

目录 背景轨迹回放 - 匀速效果图TrackPlaybackDialog.vue 代码TMapNew.vue 代码 轨迹回放 - 非匀速效果图TrackPlaybackDialog.vue 代码TMapNew.vue 代码 背景 腾讯地图轨迹回放是匀速回放的&#xff0c;但是客户要求根据现实时间&#xff0c;什么时间点在某个点位 【腾讯地图轨…

Python Day37 学习

&#xff08;补充学习几个知识点&#xff09; 1. 异常处理机制 摘自讲义 常见异常报错 2. debug 理解一下几种错误 SyntaxError 语法错误 代码不符合Python的语法规则 错误代码示例 NameError 名称错误 尝试使用一个未被定义的变量、函数或对象的名称。 TypeError 类型错…

打破建筑管理壁垒,IBMS智能系统赋能现代建筑协同增效

在建筑行业快速发展与智能化转型的进程中&#xff0c;传统建筑管理模式正面临前所未有的挑战。各子系统独立运行形成的“信息孤岛”、部门间沟通不畅导致的协作低效&#xff0c;以及管理决策缺乏数据支撑等问题&#xff0c;严重制约了建筑的运营效率与服务质量。而IBMS&#xf…

十四: 导数,数值微分,偏导数,梯度

在前一章说明损失函数的用途时,引入了梯度,导数等名词,现在我们详细了解一下这些名词 1. 导数 假如你是全程马拉松选手&#xff0c;在开始的 10 分钟内跑了 2 千米。如果要计算此时的奔跑速度&#xff0c;则为 2/10 0.2&#xff3b;千米 / 分&#xff3d;。也就是说&#xf…

深度刨析树结构(从入门到入土讲解AVL树及红黑树的奥秘)

树的概念及结构: 树是一种非线性的数据结构&#xff0c;它是由n>0个有限结点组成的一个有层次关系的集合&#xff0c;把它叫做树是因为像一个倒挂的树&#xff0c;根在上&#xff0c;叶子在下 对于树&#xff0c;每颗树都可以看成根节点和子树&#xff0c;所有的子树又可以…

Replacing iptables with eBPF in Kubernetes with Cilium

source: https://archive.fosdem.org/2020/schedule/event/replacing_iptables_with_ebpf/attachments/slides/3622/export/events/attachments/replacing_iptables_with_ebpf/slides/3622/Cilium_FOSDEM_2020.pdf 使用Cilium&#xff0c;结合eBPF、Envoy、Istio和Hubble等技术…

基于NXP例程学习CAN UDS刷写流程

文章目录 前言1.概述1.1 诊断报文 2.协议数据单元(N_PDU)2.1 寻址信息&#xff08;N_AI&#xff09;2.1.1 物理寻址2.1.2 功能寻址2.1.3 常规寻址&#xff08;Normal addressing&#xff09;2.1.4 常规固定寻址&#xff08;Normal fixed addressing&#xff09;2.1.5 扩展寻址&…

c++ 模板

测试代码。my_template.h头文件内容如下&#xff1a; #ifndef MY_TEMPLATE_HEADER_H #define MY_TEMPLATE_HEADER_H// 函数模板示例 函数模板的 T 作用域仅限于此函数 template<typename T> T my_max(T a, T b) {return (a > b) ? a : b; }// 类模板示例 类模板的 T…

HTML网页-练习float

划分 12个格子&#xff0c;第一栏为&#xff1a;人物简介&#xff1b;其他栏为人物名称&#xff1b; 使用float: left将格子左浮动。 设置格子背景颜色&#xff0c;字体颜色&#xff0c;鼠标放上去后的字体颜色和背景颜色。 <style>.title {width: 100%;overflow: hidd…

Express教程【003】:Express获取查询参数

文章目录 3、获取URL中携带的查询参数3.1 参数形式&#xff1a;查询字符串3.2 参数形式&#xff1a;动态参数3.3 参数形式&#xff1a;Json数据 3、获取URL中携带的查询参数 3.1 参数形式&#xff1a;查询字符串 1️⃣通过req.query对象&#xff0c;可以访问到客户端通过查询…

搭建最新版开源监控平台SigNoz踩的坑

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权并注明出处。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 一、前言 SigNoz 是一款开源应用程序性能监控工具&#xff0c;在往期相关文章&#xff08;文末有链接&#xff09;中…

ArcGIS应用指南:基于网格与OD成本矩阵的交通可达性分析

随着城市化进程的加速,交通系统的效率和公平性日益成为影响居民生活质量的关键因素之一。在这一背景下,如何科学评估城市区域内的交通可达性,成为了城市规划、交通管理和公共政策制定中的重要议题。作为中国东南沿海的重要港口城市,厦门以其独特的地理优势和快速的城市发展…

数据基座觉醒!大数据+AI如何重构企业智能决策金字塔(下)

1. 数据架构的量子跃迁 1.1 从线性堆叠到立体网络 传统六层架构正在经历基因重组。某智能家居企业将数据流转路径重构为三维拓扑网络后&#xff0c;新品研发周期从18个月压缩至9个月。这个改造的核心在于打破数据层间的物理隔离&#xff0c;让原始数据流能直接触达决策中枢。…

HTML5实现简洁的端午节节日网站源码

HTML5实现简洁的端午节节日网站源码 前言一、设计来源1.1 网站首页界面1.2 端午由来界面1.3 节日活动界面1.4 传统美食界面1.5 民俗文化界面1.6 登录界面1.7 注册界面 二、效果和源码2.1 动态效果2.2 源代码 结束语 HTML5实现简洁的端午节节日网站源码&#xff0c;酷炫的大气简…

复旦提出自适应Reasoning方法ARM,“能屈能伸”

为什么需要“自适应推理”&#xff1f; LLM 虽然聪明&#xff0c;但有个“学霸病”——不管题目难易&#xff0c;都要写满解题过程。比如问“11&#xff1f;”&#xff0c;它可能从宇宙起源开始推导&#xff0c;这就是论文提到的“过思考&#xff08;overthinking&#xff09;”…

如何使用 Elastic 检测恶意浏览器扩展

作者&#xff1a;Aaron Jewitt 当你的 CISO 问你某个特定浏览器扩展是否曾经被安装在任何工作站上时&#xff0c;你能多快给出正确的答案&#xff1f;恶意浏览器扩展是一个重大威胁&#xff0c;许多组织却无法管理或检测它们。本文介绍了 Elastic 信息安全团队如何使用 osquery…