栈 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) 以及后缀表达式相关推荐

  1. 模板_单调栈_AcWing_830. 单调栈_底顶递增栈

    模板_单调栈_AcWing_830. 单调栈_底顶递增栈 830. 单调栈 题目 提交记录 讨论 题解 视频讲解 给定一个长度为 NN 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 − ...

  2. 数据结构学习笔记——前、中、后缀表达式的转换(栈的应用)

    目录 一.前.中.后缀表达式定义 二.具体转换步骤 (一)中缀表达式转换为前缀表达式 (二)中缀表达式转换为后缀表达式 例题 一.前.中.后缀表达式定义 一般我们常用的中缀表达式,中缀表达式不仅依靠运 ...

  3. 0x11.基本数据结构 — 栈与单调栈

    目录 一.栈 0.AcWing 41. 包含min函数的栈 (自己造栈) 1.AcWing 128. 编辑器 (对顶栈) 2.AcWing 129. 火车进栈 3.AcWing 130. 火车进出栈问 ...

  4. 常考数据结构与算法:单调栈结构

    在数组中想找到一个数, 左边和右边比这个数小. 且离这个数最近的位置. import java.util.ArrayList; import java.util.List; import java.u ...

  5. 41. 包含min函数的栈【单调栈】

    用一个单调栈,来维护.单调栈的栈头就是最小值. class MinStack {public:/** initialize your data structure here. */stack<i ...

  6. [力扣刷题总结](栈和单调栈篇)

    文章目录 ~~~~~~~~~~~~栈~~~~~~~~~~~~ 155. 最小栈 解法1:链表 剑指 Offer 31. 栈的压入.弹出序列 解法1:模拟栈 20. 有效的括号 解法1:栈 相似题目: ...

  7. 【数据结构与算法篇】什么是后缀表达式?

    什么是后缀.前缀.以及中缀表达式呢?他与我们平时在数学中见到的表达式有什么区别?第一次看这个名词,我还是有点蒙,难理解,所以做以下笔记,以便日后复习,也希望可以帮助读者,以相互促进. 文章目录 前言: ...

  8. 数据结构与算法——前缀、中缀、后缀表达式

    目录 一.前缀表达式(波兰表达式) 二.中缀表达式 三.后缀表达式(逆波兰表达式) 1.逆波兰计算器 四.中缀转后缀 1.具体步骤 2.代码实现 一.前缀表达式(波兰表达式) 前缀表达式的运算符位于操 ...

  9. 数据结构录 之 单调队列单调栈。

    队列和栈是很常见的应用,大部分算法中都能见到他们的影子. 而单纯的队列和栈经常不能满足需求,所以需要一些很神奇的队列和栈的扩展. 其中最出名的应该是优先队列吧我觉得,然后还有两种比较小众的扩展就是单调 ...

最新文章

  1. TomCat运行struts1的编码问题
  2. OpenCV的HOG+SVM训练程序注意事项
  3. Java Optional 的 orElse() 和 orElseGet() 的区别
  4. 表单提交时submit验证非空return false没用_开发这样一个复杂的表单你需要用多久...
  5. 对node.js的net模块的一个测试
  6. Games101现代图形学入门Lecture 4: Transformation Cont知识点总结
  7. Ubuntu的中文是哪种字体?python的词云分析和 三国演义人物出场统计
  8. 10倍加速!爱奇艺超分辨模型加速实践
  9. linux 页描述符,Python描述符(descriptor)解密
  10. mac服务器 文件无法删除文件夹,macOS 10.15.4 无法删除一个空文件夹
  11. 讲讲 MySQL 中的 WAL 策略和 CheckPoint 技术
  12. 阿里第三财季:下沉市场再立功 疫情之下祸福相依
  13. TCP/IP详解 学习三
  14. Android 应用程序获得系统权限
  15. 如何将一段文本编译成C#内存程序的过程
  16. android 微信地图定位失败,微信端H5使用百度地图定位获取当前位置安卓定位不准...
  17. tp6 多关联withJoin查询
  18. ARMA模型结合PSO算法进行股票预测
  19. nemesis什么车_世界上十大最强的超级跑车,Trion Nemesis排名第一
  20. Renix签名字段详解——网络测试仪实操

热门文章

  1. Linux | 浅谈Shell运行原理【王婆竟是资本家】
  2. 论如何用添加任务计划来实现电脑自动关机
  3. 完全使用Linux替换Windows之后,我觉得自己非常愚蠢
  4. python fabric 部署falcon tcp closewait monitor function
  5. 关于网易门户网站框架结构的思考(1)
  6. springboot旅游公司网站设计 毕业设计-附源码110929
  7. Spring-Cloud-Eureka服务注册发现中心server+client案列模拟说明
  8. [软件教程]抓图软件SnagIt教程
  9. 【Python】72行实现代码行数统计,简单实用~
  10. 文献学习(part94)--Robust Subspace Clustering via Thresholding Ridge Regression