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

算法基础篇(13)单调栈

1、单调栈的定义

单调栈,顾名思义,就是具有单调性的栈。因此,它依旧是一个栈结构,只不过里面存储的数据是严格递增或者严格递减的。这种结构是很容易实现的(如下面代码)

#include <iostream> #include <stack> using namespace std; const int N = 3e6 + 10; int a[N], n; // 维护一个单调递增的栈 void test1() { stack<int> st; for(int i = 1; i <= n; i++) { // 栈里面大于等于a[i]的元素全部出栈 while(st.size() && st.top() >= a[i]) st.pop(); st.push(a[i]); } } // 维护一个单调递减的栈 void test2() { stack<int> st; for(int i = 1; i <= n; i++) { // 栈里面小于等于a[i]的元素全部出栈 while(st.size() && st.top() <= a[i]) st.pop(); st.push(a[i]); } }

那么,维护一个单调栈的意义是什么?

2、单调栈解决的问题

单调栈能帮我们解决下面四个问题:

·寻找当前元素左侧,离它最近,并且比它大的元素在哪;

·寻找当前元素左侧,离它最近,并且比它小的元素在哪;

·寻找当前元素右侧,离它最近,并且比它大的元素在哪;

·寻找当前元素右侧,离它最近,并且比它小的元素在哪。

虽然是四个问题,但是原理是一致的。所以,只要解决一个,举一反三就可以解决剩下的几个。

2.1 寻找当前元素左侧,离它最近,并且比它大的元素在哪

从左往右遍历元素,构造一个单调递减的栈。插入当前元素时:如果栈为空,则左侧不存在比当前元素大的数;如果栈非空,插入当前位置元素时的栈顶元素就是所要找的元素。

注意:因为我们要找的是最终结果的位置。因此,栈里面存的是每个元素的下标。

#include <iostream> #include <stack> using namespace std; const int N = 3e6 + 10; int a[N], n; int ret[N]; void test() { stack<int> st; for(int i = 1; i <= n; i++) { while(st.size() && a[st.top()] <= a[i]) { st.pop(); } if(st.size()) { ret[i] = st.top(); } st.push(i); } }

2.2 寻找当前元素左侧,离它最近,并且比它小的元素在哪

从左往右遍历元素,构造一个单调递增的栈。插入当前元素时:如果栈为空,则左侧不存在比当前元素小的数;如果栈非空,那么栈顶元素就是要找的元素。

注意:因为我们要找的是最终结果的位置。因此,栈里面存的是每个元素的下标。

#include <iostream> #include <stack> using namespace std; const int N = 3e6 + 10; int a[N], n; int ret[N]; void test() { stack<int> st; for(int i = 1; i <= n; i++) { while(st.size() && a[st.top()] >= a[i]) { st.pop(); } if(st.size()) { ret[i] = st.top(); } st.push(i); } }

剩下两种情况原理类似,只需要修改一下遍历顺序即可。

2.3 寻找当前元素右侧,离它最近,并且比它大的元素在哪

#include <iostream> #include <stack> using namespace std; const int N = 3e6 + 10; int a[N], n; int ret[N]; void test() { stack<int> st; for(int i = n; i >= 1; i--) { while(st.size() && a[st.top()] <= a[i]) { st.pop(); } if(st.size()) { ret[i] = st.top(); } st.push(i); } }

2.4 寻找当前元素右侧,离它最近,并且比它小的元素在哪

#include <iostream> #include <stack> using namespace std; const int N = 3e6 + 10; int a[N], n; int ret[N]; void test() { stack<int> st; for(int i = n; i >= 1; i--) { while(st.size() && a[st.top()] >= a[i]) { st.pop(); } if(st.size()) { ret[i] = st.top(); } st.push(i); } }

3、单调栈的应用

#include <iostream> #include <stack> using namespace std; const int N = 1e6 + 10; typedef long long LL; int n; LL h[N], v[N]; LL sum[N]; int main() { cin >> n; for(int i = 1; i <= n; i++) { cin >> h[i] >> v[i]; } stack<int> st1; for(int i = 1; i <= n; i++) { while(st1.size() && h[st1.top()] <= h[i]) { st1.pop(); } if(st1.size()) { sum[st1.top()] += v[i]; } st1.push(i); } stack<int> st2; for(int i = n; i >= 1; i--) { while(st2.size() && h[st2.top()] <= h[i]) { st2.pop(); } if(st2.size()) { sum[st2.top()] += v[i]; } st2.push(i); } LL ret = 0; for(int i = 1; i <= n; i++) { ret = max(ret, sum[i]); } cout << ret << endl; return 0; }
http://www.jsqmd.com/news/564602/

相关文章:

  • ManySpeech 语音处理套件:跨平台 C# 语音解决方案
  • 新手福音:基于快马平台轻松入门openclaw命令实战
  • 如何轻松获取B站4K大会员视频?这个开源工具让你一键搞定
  • Windows右键菜单重构指南:从混乱到高效的ContextMenuManager实战
  • PCIe接口卡设计原理图:124-基于XC7Z015的PCIe低速扩展底板
  • 上海航思昳商务咨询有限公司,上海全品类落户服务商,深耕上海 - 十大品牌榜
  • 3步实现GitHub全界面中文化:高效本地化工具提升开发效率指南
  • Llama-3.2V-11B-cot部署教程:双卡4090显存碎片化问题自动规避
  • 炉石传说脚本终极配置教程:3步实现高效自动化游戏体验
  • BLE项目实战:从GATT属性设计到低功耗优化,打造长续航物联网设备
  • 2026年丛林穿越项目如何选择?A公司与B公司及优乐福的性价比与服务深度对比 - 速递信息
  • 工业视觉检测避坑指南:CogBlobTool阈值设置5大常见错误及解决方案
  • CLAP在虚拟现实中的应用:3D音效分类系统
  • 2026最新上海落户推荐!创业/留学生/居转户/人才引进权威榜单发布 - 十大品牌榜
  • 怎样避免网站因 SEO 优化而被搜索引擎惩罚
  • 文脉定序系统Node.js环境配置与API调用入门
  • AI产品的五个护城河
  • 2026最新上海居转户落户推荐!权威榜单发布,助力人才扎根上海 - 十大品牌榜
  • Zotero Duplicates Merger:智能文献去重的技术突破与实践指南
  • 盒马鲜生卡回收指南:如何高效选择回收方式? - 团团收购物卡回收
  • Scarab:重构空洞骑士模组管理体验的技术实践
  • 深入解析cn.hutool.http.HttpException: Connection reset的根源与实战修复
  • COMSOL LFP磷酸铁锂电池一维P2D模型下的0.5C、1C、1.5C倍率充放电测试及阻抗输出
  • 2026最新上海创业落户/居转户/人才引进推荐!权威榜单发布 - 十大品牌榜
  • 基于SpringBoot的CLAP音频分类服务开发实战
  • 如何打破微信单设备限制:WeChatPad终极指南
  • NSC_BUILDER:Switch游戏文件管理的全能工具箱,3个技巧让你告别繁琐操作
  • SEO自动化工具如何提高网站排名_SEO自动化工具如何进行数据报告
  • DLL(Dynamic Linkable Library)的概念
  • 2026最新上海留学生落户/居转户/人才引进服务推荐 - 十大品牌榜