备战NOI 数据结构——栈与单调栈(stack) 以及后缀表达式
栈 stack
- 引入
- 栈的概念
- 代码实现
- 定义和初始化(init)
- 入栈(push)
- 出栈(pop)
- 访问栈顶元素(query)
- 查询栈的元素个数(size)
- 判断是否为空(empty)
- 清空栈(clear)
- 完整模板
- 练习题
- STL的栈
- 定义
- 常见操作
- 表达式计算
- 后缀表达式求值
- 例题(洛谷P1449)
- 中缀表达式转成后缀表达式
- 括号序列
- 单调栈
- 经典例题——Largest Rectangle in a Histogram
- 练习题
- 总结
今天我们来学习数据结构中最简单的——栈
引入
一只NOI选手csk因在NOI赛场上忘开longlonglonglonglonglong,本来打出了正解的题目只拿了30pts30pts30pts,结果离金牌只差了10pts10pts10pts,从而没有与心念的清北签约,加上高考的失利,走投无路的他来到食堂打工 (十年OI一场空,不开longlong见祖宗)
他当上了一名洗碗工,每天都要洗盘子
第一个人吃完饭后,送来了1,2,31,2,31,2,3号盘子,csk将这些盘子依次摆放,很快他就先后洗完了最上面的第3,23,23,2号盘子,随后,又一位客人过来,放上了第4,54,54,5号盘子,csk继续从上往下洗盘子,很快就洗完了
这种后进先出的数据结构,就是栈
栈的概念
栈是一种后进先出的数据结构,支持 入栈(push),出栈(pop)和访问栈顶(top) 三个操作,新入栈的元素会被后入栈的元素压到下面。
代码实现
为了方便理解,代码以结构体和函数的形式
定义和初始化(init)
我们只需用一个数组和一个变量即可实现栈
struct stack{int s[MAX];//存放栈 int top;//指向栈顶的下标,初始为0
};
入栈(push)
将该元素aaa放入栈顶,即将其放到数组的最后一个位置
void push(int a){s[++top] = a;
}
出栈(pop)
将栈顶元素弹出,仅需将数组最后一个元素的指针−1-1−1即可
void pop(){if(top)//判断栈是否为空top--;
}
访问栈顶元素(query)
直接返回数组的最后一个元素即可,栈为空返回-1
int query(){if(top)return s[top];return -1;
}
查询栈的元素个数(size)
其实元素个数就是top的值
int size(){return top;
}
判断是否为空(empty)
栈为空,返回1,否则返回0
bool empty(){if(top)return true;return false;
}
当然也可写作
bool empty(){return top == 0;
}
清空栈(clear)
将数组最后元素的指针改为0即可
void clear(){top = 0;
}
完整模板
将以上和在一起即可
struct stack{int s[MAX];//存放栈 int top;//指向栈顶的下标void push(int a){//入栈s[++top] = a;}void pop(){//出栈if(top)top--;}int query(){//访问栈顶return s[top];}int size(){//返回栈元素个数return top;}bool empty(){//判断栈是否为空return top == 0;}void clear(){//清空栈top = 0;}
};
练习题
请你实现一个栈(stack),支持如下操作:
push(x)
:向栈中加入一个数 xxx。pop()
:将栈顶弹出。如果此时栈为空则不进行弹出操作,输出Empty
。query()
:输出栈顶元素,如果此时栈为空则输出Anguei!
。size()
:输出此时栈内元素个数。
STL的栈
STL也为我们提供了栈
头文件 stack
#include <stack>
定义
stack <变量类型> 栈名;
stack <int> s;//定义一个名为s,int类型的栈
常见操作
支持的操作类似模板
stack <int> s; int x;
s.push(x);//在栈顶压入变量x
s.pop();//栈顶出栈
s.top();//返回栈顶
s.size();//返回栈元素个数
s.empty();//判断栈是否为空
s.clear();//清空栈
用STL的栈完成例题
#include<bits/stdc++.h>
using namespace std;
int main(){ios::sync_with_stdio(false);int T;cin >> T;while(T--){int n;cin >> n;stack <unsigned long long> a;string b;for(int i=1;i<=n;i++){cin >> b;if(b == "push"){unsigned long long c;cin >> c;a.push(c);}else if(b == "pop"){if(a.empty()) cout << "Empty\n";else a.pop();}else if(b == "query"){if(a.empty()) cout << "Anguei!\n";else cout << a.top() << "\n";}else cout << a.size() << "\n";}}return 0;
}
表达式计算
表达式的计算是栈的一大用处。
对于算术表达式,有前缀表达式,中缀表达式,后缀表达式(注:optoptopt为运算符,x,yx,yx,y表示数字)
中缀表达式: 就是我们数学中最常使用的一种表达式,例如 1+(2−3)1+(2-3)1+(2−3)
前缀表达式: 又称波兰式,就是将运算符放到数字前面,形如 optxyopt \ x \ yopt x y 表示 xoptyx \ opt \ yx opt y,例如+1−23+\ 1-2 \ \ 3+ 1−2 3
后缀表达式: 又称逆波兰式,就是将运算符放到数字后面,形如 xyoptx \ y \ optx y opt 表示 xoptyx \ opt \ yx opt y,例如23−1+2 \ \ 3 - 1 \ +2 3−1 +
对于计算机而言,我们最常使用的是后缀表达式,可以用栈O(len)O(len)O(len)(len表示表达式长度)求出它的值
后缀表达式求值
如何求后缀表达式的值?
- 先建立一个栈,并扫描一遍表达式
-(1)如果遇到一个数,就把它入栈
-(2)如果遇到一个运算符,就取出栈顶的两个元素进行计算,再把结果入栈- 最后栈中只剩一个数,这个数就是表达式的值
例题(洛谷P1449)
所谓后缀表达式是指这样的一个表达式:式中不再引用括号,运算符号放在两个运算对象之后,所有计算按运算符号出现的顺序,严格地由左而右新进行(不用考虑运算符的优先级)。
如:3*(5-2)+7\texttt{3*(5-2)+7}3*(5-2)+7 对应的后缀表达式为:3.5.2.-*7.+@\texttt{3.5.2.-*7.+@}3.5.2.-*7.+@。在该式中,@
为表达式的结束符号。.
为操作数的结束符号。
long long now=0;
while(cin >> ch ){if(ch=='@')break;if(ch>='0' && ch<='9'){now=now*10+ch-'0';continue;}else if(ch=='.'){sta[++top]=now;now=0;continue;}else{if(ch=='+'){sta[top-1]+=sta[top];--top;}else if(ch=='-'){sta[top-1]-=sta[top];--top;}else if(ch=='*'){sta[top-1]*=sta[top];--top;}else if(ch=='/'){sta[top-1]/=sta[top];--top;}}
}
cout<<sta[1];
中缀表达式转成后缀表达式
中缀表达式转成后缀表达式
- 先建立一个栈用来存储运算符,扫描一遍表达式的元素
-(1)如果遇到一个数,直接输出这个数
-(2)如果遇到左括号,把左括号入栈
-(3)如果遇到右括号,不断取出栈顶并输出,直到栈顶为左括号,再把左括号出栈
-(4)如果遇到运算符,只要栈顶运算符的优先级不低于新运算符,就不断输出并弹出栈顶,最后把新符号入栈- 最后依次输出并弹出栈顶,输出的序列就是一个与原中缀表达式等价的后缀表达式
代码实现,n>0n>0n>0
char ch;//中缀表达式
unsigned long long num = 0;
map <char,int> mp;//存储运算符优先级
mp['+'] = 1; mp['-'] = 1; mp['*'] = 2; mp['/'] = 2;
//有需要自己写
stack <char> s;
while(cin >> ch){if(ch >= '0' && ch <= '9')num = num*10+ch-'0';else{if(num){cout << num << " ";num = 0;}if(ch == '(')s.push(ch);else if(ch == ')'){while(s.top() != '('){cout << s.top() <<" ";s.pop();}s.pop();}else{while(!s.empty()&&mp[s.top()]>=mp[ch]){cout<< s.top() << " ";s.pop();}s.push(ch);}}
}
if(num > 0) cout << num << " ";
while(!s.empty()){cout << s.top() << " ";s.pop();
}
括号序列
栈也经常用来匹配括号序列
对于括号序列,我们可以用类似于中缀表达式的括号处理,遇到左括号入栈,遇到右括号判断栈顶是不是不这个右括号匹配,是就出栈,不是则不合法,读者可以自己模拟一下这个过程
string str;
cin >> str;
stack <char> s
for(int i=0;i<str.size();i++){if(str[i] == '(' || str[i] == '[' || str[i] == '{'){s.push(str[i]);else{if(str[i] == ')' && s.top() == '('){s.pop(); continue;}if(str[i] == ']' && s.top() == '['){s.pop(); continue;}if(str[i] == '}' && s.top() == '{'){s.pop(); continue;}cout << "no";return 0;}
}
if(s.empty() cout << "yes";
else cout << "no";
单调栈
顾名思义,就是栈内元素具有单调性的一种栈
单调栈一般用来维护最大或最小值
现以单调递减栈为例,模拟一遍单调栈的过程
- 建立一个栈,扫描一遍待操作元素
-(1)如果栈内没有元素,直接入栈
-(2)如果栈顶元素比新元素大,否则持续弹出栈顶元素,直到栈顶元素比新元素大或栈为空为止,此时被弹出元素之后第一个比它大的元素即为这个新元素,最后记得把新元素入栈
-(3)栈底的元素即为被扫过元素的最大值
stack <int> s;
//a为待操作元素
for(int i=1;i<=n;i++){while(!s.empty() && a[i] > a[s.top()])s.pop();s.push(i);
}
经典例题——Largest Rectangle in a Histogram
如图所示,在一条水平线上有 nnn 个宽为 111 的矩形,求包含于这些矩形的最大子矩形面积(图中的阴影部分的面积即所求答案)。
对于这一道题,我们发现:
如果一个矩形的高度低于上一个矩形的高度,那么上一个矩形比它高的部分在之后将毫无用处
则这些没用的部分我们就可以删掉
所以,我们可以建立一个单调栈维护矩形高度,每弹出一个矩形,就把该高度矩形宽度+1+1+1,并用宽度乘高度来更新答案
#include <iostream>
#include <cstring>
#define ull unsigned long long
using namespace std;const int MAX = 1e5+10;
int top;
long long w[MAX],s[MAX],ans;int main(){ios::sync_with_stdio(false);int n;while(cin >> n){top = 0; ans = 0;memset(w,0,sizeof(w));if(n == 0) break;for(int i=1;i<=n+1;i++){long long h; if(i == n+1) h = 0;else cin >> h;if(h > s[top] || !top){s[++top] = h;w[top] = 1;}else{long long width = 0;while(top && h <= s[top]){width += w[top];ans = max(ans,width*s[top]);top--;}s[++top] = h; w[top] = width+1;}}cout << ans << "\n";}return 0;
}
练习题
请自行完成以下练习
后缀表达式
表达式的值(提示:动态规划)
括号序列
单调栈
总结
栈(stack)作为大多数人学的第一个算法 (也是最简单的一个) 它的应用十分广泛,最主要就是用于表达式的计算 (其它好像没什么用)
本文字数5000,用时约6小时,求个三连不过分吧
备战NOI 数据结构——栈与单调栈(stack) 以及后缀表达式相关推荐
- 模板_单调栈_AcWing_830. 单调栈_底顶递增栈
模板_单调栈_AcWing_830. 单调栈_底顶递增栈 830. 单调栈 题目 提交记录 讨论 题解 视频讲解 给定一个长度为 NN 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 − ...
- 数据结构学习笔记——前、中、后缀表达式的转换(栈的应用)
目录 一.前.中.后缀表达式定义 二.具体转换步骤 (一)中缀表达式转换为前缀表达式 (二)中缀表达式转换为后缀表达式 例题 一.前.中.后缀表达式定义 一般我们常用的中缀表达式,中缀表达式不仅依靠运 ...
- 0x11.基本数据结构 — 栈与单调栈
目录 一.栈 0.AcWing 41. 包含min函数的栈 (自己造栈) 1.AcWing 128. 编辑器 (对顶栈) 2.AcWing 129. 火车进栈 3.AcWing 130. 火车进出栈问 ...
- 常考数据结构与算法:单调栈结构
在数组中想找到一个数, 左边和右边比这个数小. 且离这个数最近的位置. import java.util.ArrayList; import java.util.List; import java.u ...
- 41. 包含min函数的栈【单调栈】
用一个单调栈,来维护.单调栈的栈头就是最小值. class MinStack {public:/** initialize your data structure here. */stack<i ...
- [力扣刷题总结](栈和单调栈篇)
文章目录 ~~~~~~~~~~~~栈~~~~~~~~~~~~ 155. 最小栈 解法1:链表 剑指 Offer 31. 栈的压入.弹出序列 解法1:模拟栈 20. 有效的括号 解法1:栈 相似题目: ...
- 【数据结构与算法篇】什么是后缀表达式?
什么是后缀.前缀.以及中缀表达式呢?他与我们平时在数学中见到的表达式有什么区别?第一次看这个名词,我还是有点蒙,难理解,所以做以下笔记,以便日后复习,也希望可以帮助读者,以相互促进. 文章目录 前言: ...
- 数据结构与算法——前缀、中缀、后缀表达式
目录 一.前缀表达式(波兰表达式) 二.中缀表达式 三.后缀表达式(逆波兰表达式) 1.逆波兰计算器 四.中缀转后缀 1.具体步骤 2.代码实现 一.前缀表达式(波兰表达式) 前缀表达式的运算符位于操 ...
- 数据结构录 之 单调队列单调栈。
队列和栈是很常见的应用,大部分算法中都能见到他们的影子. 而单纯的队列和栈经常不能满足需求,所以需要一些很神奇的队列和栈的扩展. 其中最出名的应该是优先队列吧我觉得,然后还有两种比较小众的扩展就是单调 ...
最新文章
- TomCat运行struts1的编码问题
- OpenCV的HOG+SVM训练程序注意事项
- Java Optional 的 orElse() 和 orElseGet() 的区别
- 表单提交时submit验证非空return false没用_开发这样一个复杂的表单你需要用多久...
- 对node.js的net模块的一个测试
- Games101现代图形学入门Lecture 4: Transformation Cont知识点总结
- Ubuntu的中文是哪种字体?python的词云分析和 三国演义人物出场统计
- 10倍加速!爱奇艺超分辨模型加速实践
- linux 页描述符,Python描述符(descriptor)解密
- mac服务器 文件无法删除文件夹,macOS 10.15.4 无法删除一个空文件夹
- 讲讲 MySQL 中的 WAL 策略和 CheckPoint 技术
- 阿里第三财季:下沉市场再立功 疫情之下祸福相依
- TCP/IP详解 学习三
- Android 应用程序获得系统权限
- 如何将一段文本编译成C#内存程序的过程
- android 微信地图定位失败,微信端H5使用百度地图定位获取当前位置安卓定位不准...
- tp6 多关联withJoin查询
- ARMA模型结合PSO算法进行股票预测
- nemesis什么车_世界上十大最强的超级跑车,Trion Nemesis排名第一
- Renix签名字段详解——网络测试仪实操
热门文章
- Linux | 浅谈Shell运行原理【王婆竟是资本家】
- 论如何用添加任务计划来实现电脑自动关机
- 完全使用Linux替换Windows之后,我觉得自己非常愚蠢
- python fabric 部署falcon tcp closewait monitor function
- 关于网易门户网站框架结构的思考(1)
- springboot旅游公司网站设计 毕业设计-附源码110929
- Spring-Cloud-Eureka服务注册发现中心server+client案列模拟说明
- [软件教程]抓图软件SnagIt教程
- 【Python】72行实现代码行数统计,简单实用~
- 文献学习(part94)--Robust Subspace Clustering via Thresholding Ridge Regression