单调栈(C/C++)

引言:

单调队列和单调栈都是一种数据结构,应用十分广泛,在蓝桥杯、ICPC、CCPC等著名编程赛事都是重点的算法,今天博主将自己对单调栈与单调队列的理解以及刷题的经验,用一篇博客分享给大家,希望对大家有所帮助,它们用于解决类似“寻找最大值与最小值”这样的问题。它们的区别在于如何维护数据的单调性。、

  1. 单调栈(Monotonic Stack):

    • 单调栈是一种栈数据结构,只能在栈顶进行插入和删除操作
    • 单调栈的特点是栈中的元素按照一定的单调性排列,常用的有单调递增和单调递减。
    • 在插入新元素时,如果新元素破坏了当前的单调性,则从栈顶删除一部分元素,直到满足单调性要求。这样可以保证栈中的元素保持单调性。
    • 单调栈的典型应用是在寻找下一个更大/更小元素的问题。
  2. 单调队列(Monotonic Queue):

    • 单调队列是一个双端队列,支持在队列两端进行插入和删除操作
    • 单调队列的特点是队列中的元素按照一定的单调性排列,常用的有单调递增和单调递减。
    • 在插入新元素时,如果新元素破坏了当前的单调性,则在队尾删除一部分元素,直到满足单调性要求。这样可以保证队列中的元素保持单调性。
    • 单调队列的典型应用是在滑动窗口中寻找最大/最小值的问题。

单调队列和单调栈都是用于维护数据的单调性,但单调队列是双端队列,用于在滑动窗口中寻找最大/最小值,而单调栈是栈数据结构,用于寻找下一个更大/更小元素

下面我们对单调栈进行深度解析


单调栈:

单调栈是一种数据结构,通常用于解决一些与序列相关的问题,特别是在需要找到元素在序列中的「下一个更大元素」或「下一个更小元素」的情况下。单调栈可以用于在线性时间复杂度内解决这些问题。


单调栈分为单调递增栈和单调递减栈两种类型:

1.单调递增栈:

栈中元素从栈底到栈顶递增。在处理序列时,当遇到一个元素时,如果该元素比栈顶元素大,就可以将栈顶元素出栈,直到栈为空或者栈顶元素大于等于当前元素。这样,栈中的元素就是在当前元素之前且比当前元素小的元素。


2.单调递减栈:

栈中元素从栈底到栈顶递减。在处理序列时,当遇到一个元素时,如果该元素比栈顶元素小,就可以将栈顶元素出栈,直到栈为空或者栈顶元素小于等于当前元素。这样,栈中的元素就是在当前元素之前且比当前元素大的元素。

使用单调栈的一般步骤如下:

1.创建一个空栈。
2.遍历待处理的序列,对于每个元素执行以下操作:

1.如果当前元素比栈顶元素大(或小,取决于是递增栈还是递减栈),则持续将栈顶元素出栈,直到栈为空或者栈顶元素满足某种条件(例如比当前元素大或小)。
2.记录弹出的元素,说明他是单调递减栈或单调递增栈第一个不满足的元素,可以在此元素根据题意进行操作
3.如果栈不为空,比较当前元素与栈顶元素的大小:
4..将当前元素入栈。

单调栈常用于解决一些数组或序列相关的问题,如找到下一个更大元素、下一个更小元素。

模板奉上:

第一种使用stack
stack<int> st; // 单调栈,存储元素的下标
nums[n]=-1; //多加一个-1元素,防止到最后栈中还是单调递增栈,未能更新最大值
//单调递减栈就是nums[n]=0x3f3f3f;for (int i = 0; i < n; i++) {while (!st.empty() && nums[i] <= nums[st.top()]) {//不满足单调递增栈,就开始更新res[st.top()] = i; // 弹出元素直到满足单调递增st.pop();}st.push(i);//下标入队
}
第二种手写一个单调栈
int work3(int h[]) {// stk 从下标 1 开始存储int top = 0, res = 0;// 遍历到 m + 1 的位置// 因为是在出栈时统计,为保证遍历结束时所有下标都会被统计,默认 m + 1 位置存储 0for (int i = 1; i <= m + 1; ++ i ) {while (top && h[stk[top]] >= h[i]) {int l = stk[top -- ];//此处添加代码根据题意进行操作}stk[ ++ top] = i;}return res;
}

实战演练——单调栈练习题

【模板】单调栈 - 洛谷

#include<iostream>
#include<stack>
using namespace std;
int n;
const int N=3e6+5;
int a[N],res[N];
stack<int> stk;
int main(){cin>>n;for(int i=1;i<=n;i++){cin>>a[i];}for(int i=n;i>=1;i--){while(!stk.empty()&&a[stk.top()]<=a[i]){stk.pop();}res[i]=stk.empty()?0:stk.top();stk.push(i);}for(int i=1;i<=n;i++){cout<<res[i]<<" ";}return 0;
}

131. 直方图中最大的矩形 - AcWing题库

#include<iostream>
#include<stack>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n;
ll a[N];
ll res;
int main(){while(cin>>n&&n){for(int i=0;i<n;i++){cin>>a[i];}a[n]=-1;//单调递增栈,不要忘记最后一个赋值为-1,不然跟上图里面的数据不会更新res=0;stack<int> st;//使用STL的stackfor(int i=0;i<=n;i++){while(!st.empty()&&a[st.top()]>=a[i]){//每当不符合说明要开始更新最大面积了int index=st.top();//每次弹出来跟当前不满足的值进行计算最大面积st.pop();int l=st.empty()?-1:st.top();res=max(res,(i-l-1)*a[index]);//i-l-1为宽,a[index]为高}st.push(i);//下标入栈}cout<<res<<endl;}return 0;
}

1574. 接雨水 - AcWing题库

#include<iostream>
#include<stack>
using namespace std;
const int N=1e5+5;
int n;
int a[N];
int ans;
stack<int> st;
int main(){cin>>n;for(int i=0;i<n;i++){cin>>a[i];}//由于寻找第一个右边比他大的元素,为单调递减栈,不需要再加一个-1,此题边界不可以接雨水,所以不用赋值最后一位for(int i=0;i<n;i++){while(!st.empty()&&a[st.top()]<a[i]){//当前值大于栈顶元素,开始更新雨水面积int dex=st.top();st.pop();int left=st.empty()?i-1:st.top();//i-1与上题不同具体问题具体分析,此题若栈中没有元素,自己自成一个,不可能接住水与下面(i-left-1),left带入为计算0int h=min(a[left],a[i])-a[dex];//高度ans+=h*(i-left-1);}st.push(i);}cout<<ans<<endl;return 0;
}

1413. 矩形牛棚 - AcWing题库

这一题之前蓝桥杯每日一题写过题解,可以看过来:AcWing 1413. 矩形牛棚(每日一题)-CSDN博客


总结:

单调栈在题目中应用很广泛,是一名算法选手所必须掌握的基础算法,在题目中遇到寻找最大最小的元素,或者对元素进行找最大最小的问题可以考虑单调栈,单调栈主要适用于一些需要找到“下一个更大(或更小)元素”的问题。通过维护一个单调递增(或递减)的栈,可以高效地找到下一个更大(或更小)元素。在实际应用中,需要注意栈的边界条件及特殊情况的处理。单调栈的时间复杂度通常为O(n),其中n为元素的个数。利用单调栈可以在题目规定的时间可以解决问题。博主在学习单调栈的过程中将自己由开始学习到逐渐理解,期间的一些疑问以及自己所收获的凝结成此篇文章,望对大家有所帮助,文章尚有不足之处,恳请各位大佬指出,感激不尽。

下一篇更新:优先队列

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

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

相关文章

【UI】element-ui的el-dialog的遮罩层在模态框的前面bug

最近在写element ui 的时候使用dialog组件&#xff0c;偶然出现了这种情况 原因&#xff1a; 是因为遮罩层插入进了body标签下&#xff0c;z-index高于当前父元素。 解决&#xff1a;在el-dialog标签里加上:modal-append-to-body"false"就可以了。 饿了么官网文档&a…

Ardupilot OpenIPC 基于WFB-NG构架分析和数据链路思考

Ardupilot & OpenIPC & 基于WFB-NG构架分析和数据链路思考 1. 源由2. OpenIPC安装2.1 安装2.2 配置2.2.1 天空端配置文件2.2.2 地面端配置文件 2.3 当前配置选择 3. WFB-NG安装3.1 RTL8812AU安装3.1.1 驱动安装3.1.2 定位设备 3.2 wfb-ng安装3.2.1 传输层安装3.2.2 配置…

照片相似性搜索引擎Embed-Photos;赋予大型语言模型(LLMs)视频和音频理解能力;OOTDiffusion的基础上可控制的服装驱动图像合成

✨ 1: Magic Clothing Magic Clothing是一个以可控制的服装驱动图像合成为核心的技术项目&#xff0c;建立在OOTDiffusion的基础上 Magic Clothing是一个以可控制的服装驱动图像合成为核心的技术项目&#xff0c;建立在OOTDiffusion的基础上。通过使用Magic Clothing&#xf…

Jenkins CI/CD 持续集成专题二 Jenkins 相关问题汇总

一 问题一 pod [!] Unknown command: package 1.1 如果没有安装过cocoapods-packager&#xff0c;安装cocoapods-packager&#xff0c;sudo gem install cocoapods-packager 1.2 如果已经安装cocoapods-packager&#xff0c;还是出现上面的错误&#xff0c;有可能是pod的安…

防、治、管融合一体化旅居健康守护系统

随着人口老龄化趋势的加剧和人们生活水平的提高&#xff0c;养老服务需求不断增长。旅居养老作为一种新型的养老模式&#xff0c;逐渐受到广大老年人的青睐。在生命健康服务运营平台中&#xff0c;为了给老人提供全方位、个性化的生命健康服务&#xff0c;我们平台也特意开设了…

31 信号量

概念 共享内存在通信的过程中&#xff0c;没有任何保护机制。当A进程写入了一部分&#xff0c;就被B、拿走了&#xff0c;导致双方发和收的数据不完整&#xff0c;数据不一致问题 1.A和B看到同一份资源&#xff0c;共享资源&#xff0c;如果不加保护&#xff0c;会导致数据不…

PyCharm添加外部工具

QtDesigner 可视化UI设计客户端工具 路径&#xff1a;File | Settings | Tools | External Tools点号&#xff0c;给External Tools组添加一个条目&#xff0c;填写如下内容 Name&#xff1a;QtDesignerProgram&#xff1a;C:\Users\用户名\AppData\Local\Programs\Python\Py…

在誉天学习云计算HCIE,担心考试考不过?

誉天定制化课程内容覆盖了所有考试重点&#xff0c;可以系统地掌握理论与实践知识。 对于笔试&#xff0c;类似于备考驾照理论学习阶段&#xff0c;誉天为大家提供在线模拟测试系统&#xff0c;帮助大家掌握云计算笔试考点。笔试通过后&#xff0c;18个月内&#xff08;一年半…

Docker 哲学 - docker save | load | export | import 及实践

当我们说 "归档文件包含了容器的所有文件&#xff0c;但不包含容器的元数据和配置"&#xff0c;我们是指 docker export 命令导出的 tar 归档文件包含了容器的文件系统中的所有文件&#xff0c;包括应用程序、库、数据等。但是&#xff0c;这个归档文件不包含关于容器…

Python 网络与并发编程(一)

文章目录 并发编程介绍串行、并行与并发的区别进程、线程、协程的区别进程线程协程 并发编程解决方案同步和异步介绍 并发编程介绍 串行、并行与并发的区别 有任务A、B、C&#xff0c;一个CPU去执行他们&#xff0c;有几种方式 1、一个cpu按顺序执行ABC&#xff0c;这就是串行…

go语言并发实战——日志收集系统(六) 编写日志收集系统客户端

上节回顾 在上一篇文章中我们介绍了编写客户端的四个步骤&#xff0c;分别是&#xff1a; 读取配置文件&#xff0c;寻找日志路径初始化服务根据日志路径l来收集日志将收集到的日志发送Kafka中 关于上述的内容博主画了一个思维导图(有点丑&#xff0c;大家勉强看看&#xff0…

燃冬之yum、vim和你

了解了很多指令和权限&#xff0c;搞点真枪实弹来瞅瞅 学Linux不是天天就在那掰扯指令玩&#xff0c;也不是就研究那个权限 准备好迎接Linux相关工具的使用了么码农桑~ yum 软件包 什么是软件包呢&#xff1f; 首先来举个生活中常见点的例子&#xff1a;比如我的手机是华为…

Tensor张量的属性dim、type、size、shape、numel的使用方法介绍

本文重点 Tensor张量有一些常用的属性&#xff0c;我们可以通过这些基本的属性判断张量的类型&#xff0c;维度&#xff0c;以及元素个数&#xff0c;本节课程我们将对tensor属性进行简单的介绍。 代码 import torch import numpy as np atorch.Tensor(2,3,4) print(a) prin…

【论文笔记 | 异步联邦】 FedBuff

1. 论文信息 Federated Learning with Buffered Asynchronous Aggregation&#xff0c;International Conference on Artificial Intelligence and Statistics&#xff0c;2022&#xff0c;ccfc 2. introduction 2.1.1. 背景&#xff1a; 同步 FL &#xff0c;随训练过程中…

AI视频分析技术的常用开源模型及TSINGSEE青犀AI视频识别分析能力介绍

AI视频分析技术是指利用人工智能技术来对视频数据进行分析和处理的技术。开源模型是指可以免费获取和使用的代码模型&#xff0c;可以帮助开发人员快速构建和部署AI视频分析应用程序。 以下是一些业内常用的用于AI视频分析技术的开源模型&#xff1a; OpenCV&#xff1a;Open…

给字符串添加加粗标签(AC自动机+Python)

可以暴力解决&#xff0c;但是为了锻炼一下ac自动机的编程&#xff0c;我们使用ac自动机。 ac自动机主要维护两个列表&#xff0c;一个列表ch&#xff0c;ch[f][idx]表示从父节点f向idx这个方向走&#xff0c;走到的节点。另一个列表nex&#xff0c;nex[i]表示节点i回跳边的节…

01 【哈工大_操作系统】x86_64 常用寄存器大全

在学习CPU底层技术的时候&#xff0c;难免会接触到各式各样的寄存器。尤其是在使用汇编语言编写操作系统时&#xff0c;寄存器更是必不可少的。因此&#xff0c;这篇文章将来详细聊聊 x86_64 架构中的所有寄存器&#xff0c;按照从 常用->不常用 的顺序来进行介绍。 首先&a…

Spark-机器学习(4)回归学习之逻辑回归

在之前的文章中&#xff0c;我们来学习我们回归中的线性回归&#xff0c;了解了它的算法&#xff0c;知道了它的用法&#xff0c;并带来了简单案例。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请…

Syncovery for Mac v10.14.3激活版:文件备份和同步工具

Syncovery for Mac是一款高效且灵活的文件备份与同步工具&#xff0c;专为Mac用户设计&#xff0c;旨在确保数据的安全性和完整性。该软件支持多种备份和同步方式&#xff0c;包括本地备份、网络备份以及云备份&#xff0c;用户可以根据实际需求选择最合适的方案。 Syncovery f…

信息系统项目管理师0062:需求分析(5信息系统工程—5.1软件工程—5.1.2需求分析)

点击查看专栏目录 文章目录 5.1.2需求分析1.需求的层次2.需求过程3.UML4.面向对象分析记忆要点总结5.1.2需求分析 软件需求是指用户对新系统在功能、行为、性能、设计约束等方面的期望。根据IEEE的软件工程标准词汇表,软件需求是指用户解决问题或达到目标所需的条件或能力,是…