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

Kevin的算法笔记(2)栈和队列①

栈是一种重要的数据结构,它具有后进先出(LIFO)的性质,因此也被简称为LIFO表。

有一种特殊的栈叫做单调栈,它要求维护栈中每个数的大小是单调的。比如写一个单调栈,在压入每一个数时,都要先把栈上面每个大于等于它的数弹出来。这样做的好处是,对于一个序列里的每个数,我们可以很轻松地知道它前面的第一个比它小的数和它后面第一个比它小的数,即它被压入栈时下面的第一个数,和它被弹出时压入栈的数。换句话说,对于维护好的单调栈,取出比任意一个元素大(小)的前后第一个元素的时间复杂度是o(1)

队列

与栈相反,队列具有先进先出(FIFO)的性质,因此也被叫做FIFO表。

通常意义上的队列只能从队尾添加元素、从队首删除元素,两边都能增删元素的队列被称作双端队列。

我们可以用与单调栈同样的思路创造一个单调队列:从队首和队尾都可以出队,只能从队尾入队,要求维护队列的单调性。取出维护好的单调队列里的最大(最小)值的时间复杂度是o(1),因此单调队列在解决滑动窗口等问题时非常好用。

下面我们通过一些题目来理解一下栈和队列。

1.栈和排序

这道题的思路其实很符合直觉,我们需要预处理每个元素到最后一个元素的区间最大值,当把每个元素入栈时,如果这个元素超过了后面所有元素的最大值,就要立刻把它弹出来。

示例代码如下:

查看代码
#include <iostream>
#include <vector>
#include <stack>
using namespace std;int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int n;cin >> n;vector<int> p(n);for (int i = 0; i < n; ++i) {cin >> p[i];}// 预处理后缀最大值,suf_max[i]表示p[i..n-1]中的最大值vector<int> suf_max(n + 1);suf_max[n] = 0; // 哨兵,处理所有元素入栈后剩余最大值为0for (int i = n - 1; i >= 0; --i) {suf_max[i] = max(p[i], suf_max[i + 1]);}stack<int> st;vector<int> ans;for (int i = 0; i < n; ++i) {st.push(p[i]);// 只要栈顶比剩下元素都大,就弹出while (!st.empty() && st.top() >= suf_max[i + 1]) {ans.push_back(st.top());st.pop();}}// 输出结果for (int i = 0; i < ans.size(); ++i) {if (i > 0) cout << ' ';cout << ans[i];}cout << endl;return 0;
}

2.POJ 1028--Web Navigation

根据题干要求,创建一个前向栈和一个后向栈即可。每当执行BACK操作的时候,前向栈入栈一个,后向栈出栈一个,反之同理。如果执行VISIT操作,把前向栈清空。

示例代码如下:

查看代码
#include<bits/stdc++.h>
using namespace std;int main(){stack <string> forw;stack <string> back;string op;while(cin>>op&&op!="QUIT"){if(op=="VISIT"){string url;cin>>url;back.push(url);cout<<url<<endl;while(!forw.empty())forw.pop();}else if(op=="BACK"){if(!back.empty()){forw.push(back.top());back.pop();if(!back.empty())cout<<back.top()<<endl;else{cout<<"http://www.acm.org/"<<endl;	} }else cout<<"Ignored"<<endl;}else if(op=="FORWARD"){if(!forw.empty()){cout<<forw.top()<<endl;back.push(forw.top());forw.pop();}else cout<<"Ignored"<<endl;}}
} 

3.POJ 2823--Sliding Window

维护一个单调增的队列可以获得窗口内的最小值,维护一个单调减的队列可以获得一个窗口的最大值,注意将过期的元素删除。

下面给出两种实现方法,一种是用数组模拟栈,一种是使用双端队列。

查看代码
 #include <cstdlib>
#include <cstring>
#include <iostream>
constexpr int MAXN = 1000100;
using namespace std;
int q[MAXN], a[MAXN];
int n, k;void getmin() {  // 得到这个队列里的最小值,直接找到最后的就行了int head = 0, tail = -1;for (int i = 1; i < k; i++) {while (head <= tail && a[q[tail]] >= a[i]) tail--;q[++tail] = i;}for (int i = k; i <= n; i++) {while (head <= tail && a[q[tail]] >= a[i]) tail--;q[++tail] = i;while (q[head] <= i - k) head++;cout << a[q[head]] << ' ';}
}void getmax() {  // 和上面同理int head = 0, tail = -1;for (int i = 1; i < k; i++) {while (head <= tail && a[q[tail]] <= a[i]) tail--;q[++tail] = i;}for (int i = k; i <= n; i++) {while (head <= tail && a[q[tail]] <= a[i]) tail--;q[++tail] = i;while (q[head] <= i - k) head++;cout << a[q[head]] << ' ';}
}int main() {cin.tie(nullptr)->sync_with_stdio(false);cin >> n >> k;for (int i = 1; i <= n; i++) cin >> a[i];getmin();cout << '\n';getmax();cout << '\n';return 0;
}
查看代码
 #include <bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define MAXN 1000005
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
struct node{int pos,val;
}a;
deque<node>maxq,minq;
int maxans[MAXN],minans[MAXN];
int main(){int n,k,x;while(~scanf("%d%d",&n,&k)){while(!maxq.empty())maxq.pop_back();while(!minq.empty())minq.pop_back();rep(i,1,k-1){scanf("%d",&x);while(!maxq.empty()&&maxq.back().val<=x)maxq.pop_back();while(!minq.empty()&&minq.back().val>=x)minq.pop_back();a.pos=i,a.val=x;maxq.push_back(a);minq.push_back(a);}rep(i,k,n){while(!maxq.empty()&&maxq.front().pos<i-k+1)maxq.pop_front();while(!minq.empty()&&minq.front().pos<i-k+1)minq.pop_front();scanf("%d",&x);while(!maxq.empty()&&maxq.back().val<=x)maxq.pop_back();while(!minq.empty()&&minq.back().val>=x)minq.pop_back();a.pos=i,a.val=x;maxq.push_back(a);minq.push_back(a);maxans[i]=maxq.front().val; minans[i]=minq.front().val;}rep(i,k,n){printf("%d%c",minans[i],i==n?'\n':' ');}rep(i,k,n){printf("%d%c",maxans[i],i==n?'\n':' ');}}return 0;
}

 

http://www.jsqmd.com/news/710871/

相关文章:

  • 第四十三周周报
  • GESP学习考试必读((一)、《粗心怪其实是“漏洞怪”》)
  • 手把手教你用Python生成COE文件,为FPGA以太网通信初始化MAC地址
  • 告别Inspect!用微软官方推荐的Accessibility Insights搞定WinApp自动化测试元素定位
  • 别再乱用get_event_loop了!深入Python asyncio源码,看透事件循环的线程隔离机制
  • 自回归生成图像检测:D3QE方法解析与应用
  • FanControl深度解析:如何通过Windows开源工具实现精准风扇控制
  • DeepSeek总结的数据库外部表
  • STM32物联网云监控智能报警器(MQ-2烟雾/火焰/DHT11温湿度/红外)
  • Qt项目构建进阶:从.pro到.pri,详解那些藏在qmake里的‘黑魔法’与避坑指南
  • 保姆级教程:用YOLOv8/RT-DETR实现工地安全帽检测与人员追踪(附完整代码)
  • Docker镜像拉取总失败?除了换源,试试搭建自己的私有镜像缓存仓库(Harbor实战)
  • LLM分类器架构与特征工程实践对比
  • 2026年国内GEO行业入局指南:主流服务商实力解析与代理合作全攻略 - GEO优化
  • 仅剩48小时!Docker官方认证AI工程师考试大纲已同步更新至v2026.1,附赠3套高仿真模考卷(含动态权重评分系统)
  • C#面向对象
  • 如何快速掌握SubFinder字幕查找器:新手终极实战指南
  • 苍穹外卖订单状态流转设计:从下单到完成的全链路解析
  • 3步终极指南:免费开源工具G-Helper快速解决华硕笔记本性能瓶颈
  • 保姆级教程:将QtMqtt库集成到你的QT Creator项目中(以SimpleClient为例)
  • 艾尔登法环 DirectX 闪退怎么办?2026最新修复步骤与原因排查
  • 中文心理咨询对话数据集架构解析与AI心理健康应用实现
  • Vosk-API深度解析:从源码编译到生产部署的完整技术指南
  • Sunshine游戏串流终极教程:5步搭建你的私人云游戏平台
  • 音乐解锁完整指南:如何在浏览器中免费解密加密音乐文件
  • Cursor编辑器AI代码导航规则配置实战:提升开发效率的智能跳转指南
  • 强化学习探索策略优化与GRPO框架实践
  • JVM 学习第七天:JVM 终结篇——执行引擎+内存模型+调优实战+大厂面试压轴题(无重复)
  • 大语言模型与信息检索工具链的工程实践
  • 第二十三篇技术笔记:郭大侠学DoIP - 扒扒DoIP报文的“底裤”