栈 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. Matlab中bwlabel函数的使用
  2. 小米2+android版本,小米2S能刷Android4.4系统吗 小米2S刷Android4.4.2教程
  3. 四十四、Hexo搭建自己的博客
  4. 安装mysql error 1045_安装MySQL出现1045错误
  5. 部署在CloudFoundry上的nodejs如何正确使用port环境变量
  6. javafx2_JavaFX 2 GameTutorial第5部分
  7. 风讯CMS常见问题锦集
  8. Report Style
  9. 华为云MVP付健权:从机械工程师到AI开发者的华丽转身
  10. 51单片机指针c语言,单片机C语言教程:C51指针的使用
  11. 常见的几种网络故障案例分析与解决
  12. windows录屏_工具推荐:这些录屏软件既免费又好用
  13. Python开发Http代理服务器 - socketref,呆在autonavi.com - C++博客
  14. 8000401a 错误 及解决办法
  15. GIS数据处理与应用开发一站式解决方案
  16. exls表格搜索快捷键_excel表格查找快捷键|excel表格的常用功能快捷键介绍
  17. ovs vlan tag管理
  18. 关于jd-gui启动报This program requires Java 1.8+的错误
  19. Vulkan_间接绘制(vkCmdDrawIndexedIndirect)
  20. Markdown语法大全(超级版)

热门文章

  1. IE、360浏览器兼容解决
  2. 从零开始之驱动发开、linux驱动(十一、linux的中断框架和详细调用流程)
  3. “Hello World!”团队第六周的第五次会议
  4. 1、Java中“并发编程”详解【voliate、synchronized、JMM内存模型、原子类操作Atomic..、CAS原理】
  5. 在计算机技术培训班上的讲话,在电脑培训班上的讲话.doc
  6. 【转载】SnagIt使用说明
  7. Windows修改C盘下的用户(Users)文件夹下的汉字文件夹
  8. Facebook 平台详解
  9. JavaScript基础(一)(编程语言,计算机基础,初始JavaScript,JavaScript注释,输入输出语句,变量的概念,变量的使用,数据类型,解释型语言和编译型语言)
  10. Linux 之父“开炮”!曾喊 AMD 真香,今炮轰 AMD:怒批 fTPM “愚蠢”、“破玩意儿”...