文章目录
栈
什么是栈
栈是一种先进后出的数据结构, 就好比我们生活中的乒乓球桶, 只有一端开口。它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。 我们学习递归的时候, 在计算机内部就是用栈实现的递归。在计算机中,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算,这一端被称为栈顶,相对地,把另一端称为栈底。
写一个栈
按照栈的基本定义,我们可以自己动手来写一个保存整数的栈。
在动手之前,先来规划需要实现哪些功能,简单总结如下:
操作 | 函数 | 解释 |
---|---|---|
入栈 | push(x) | 将x元素入栈 |
出栈 | pop() | 弹出栈的第一个元素,返回值为栈顶元素值 |
元素个数 | size() | 获取栈中的元素个数,返回int |
获取栈顶元素 | top() 获取栈顶元素的值 |
我们可以用一个数组 stackArray ,来记录栈的元素。
用一个下标 index ,记录当前栈顶的位置,有了这个位置,我们可以获取栈顶元素,也可以获取元素数量。
具体实现如下:
#include <bits/stdc++.h>
using namespace std;
int stackArray[1000], index = -1;
void push(int x) {
index++;
stackArray[index] = x;
}
int pop() {
int value = stackArray[index];
index--;
return value;
}
int top() {
return stackArray[index];
}
int size() {
return index + 1;
}
int main() {
push(1);
push(2);
push(3);
cout << "顶:" << top() << endl;
cout << "大小:" << size() << endl;
pop();
cout << "顶:" << top() << endl;
cout << "大小:" << size() << endl;
pop();
cout << "顶:" << top() << endl;
cout << "大小:" << size() << endl;
pop();
return 0;
}
运行结果如下:
顶:3
大小:3
顶:2
大小:2
顶:1
大小:1
stack的基本操作
在 C++ 的标准库中, 有封装好的栈 stack , stack 是一个模板类,定义 stack 的示例代码如下:
stack<类型> 对象:
stack<int> s;
操作 | 代码 | 解释 |
---|---|---|
入栈 | s.push(x) | 将x元素入栈 |
出栈 | s.pop() | 弹出栈的第一个元素,并不会返回元素的值 |
栈顶元素 | s.top() | 获取栈的第一个元素 |
元素个数 | s.size() | 获取栈中的元素个数,返回int |
判空 | s.empty() | 栈是否为空,返回bool,相当于s.size() == 0 |
stack的示例
#include <bits/stdc++.h>
using namespace std;
int main() {
stack<int> s;
s.push(1);
s.push(2);
s.push(3);
cout << "顶:" << s.top() << endl;
cout << "大小:" << s.size() << endl;
s.pop();
cout << "顶:" << s.top() << endl;
cout << "大小:" << s.size() << endl;
s.pop();
cout << "顶:" << s.top() << endl;
cout << "大小:" << s.size() << endl;
s.pop();
return 0;
}
输出:
顶:3
大小:3
顶:2
大小:2
顶:1
大小:1
课堂练习:操作栈
有一个初始为空的栈,我们对这个栈进行n次操作,操作共分为2种:
- 1 x(将数字 x 放入栈)
- 2(将栈顶元素弹出)
对于第2种操作,你需要把弹出的这个数字输出,如果进行操作2时,栈为空,则输出"empty"。
例如:n = 5,对应的操作为:
1 123 (操作后栈里面的元素为:123)
1 234(操作后栈里面的元素为:123, 234)
2(输出:234,操作后栈里面的元素为:123)
2(输出:123,操作后栈里面的元素为:空)
2(输出:empty)
对应后面3个第2类操作,你的程序需要输出,
234
123
empty
输入格式
第一行:1个数n(1 <= n <= 10000) 后面n行:每行1种操作,1 X或者2(0 <= x <= 10000)
输出格式
对应所有操作2,输出被弹出的数或者"empty"
输入样例
5
1 123
1 234
2
2
2
输出样例
234
123
empty
程序1答案
#include<bits/stdc++.h>
using namespace std;
int n,p,x;
stack<int> k;
int main(){
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>p;
if(p==1){
cin>>x;
k.push(x);
}
else{
if(k.empty())
cout<<"empty"<<endl;
else{
cout<<k.top()<<endl;
k.pop();
}
}
}
}
题解:定义一个栈,按照题目要求操作,遇到 1 则入栈,遇到 2 则先输出栈顶元素,再让元素出栈。如果空了就输出“empty”。
课后作业:数字游戏
现在有n个数字依次进入一个栈,每个数字a进入栈的时候,如果栈顶元素小于a,则会将栈顶元素弹出,新的栈顶元素如果仍然小于a,则会将新的栈顶元素继续弹出,直到栈顶元素大于等于a为止,a才会加入栈。问n个数字依次进入后,最后栈里的数字依次是?
输入格式
输入第一行一个整数n(n<=100000),表示有n个数字依次进入栈。 接下来n行,每行一个整数,表示第i个数字。
输出格式
若干行,表示最后栈中的数字。
输入样例
5
5
3
2
4
1
输出样例
5
4
1
程序2答案
#include <bits/stdc++.h>
using namespace std;
int n;
stack<int> a;
int m;
int s[100000];
int j;
int main(){
scanf("%d",&n);
while(n--){
cin>>m;
if(a.empty()){
a.push(m);
}
else{
while(!a.empty()&&a.top()<m){
a.pop();
}
a.push(m);
}
}
while(!a.empty()){
s[j]=a.top();
a.pop();
j++;
}
for(int i=j-1;i>=0;i--){
cout<<s[i]<<endl;
}
}
题解:本题描述的这种栈,有专门的命名叫做单调栈。因为栈中的元素是递减的,所有本题只需按照题目要求,处理栈的 push 和 pop 即可。但需要特别注意栈为空时,要将下一个push。
进阶习题:合法括号序列 V1
有一个括号序列,现在要检测一下它是否是合法的括号序列
合法括号序列的定义是:
1.空序列是合法括号序列。
2.如果S是合法括号序列,那么(S)是合法括号序列。
3.如果A和B都是合法括号序列,那么AB是合法括号序列。
输入格式
输入一行,长度为N的括号序列S(0<=N<=50000,S只包括()这2种字符)
输出格式
输出一行,1表示括号序列合法,0表示括号序列不合法
输入样例
(())(
输出样例
0
程序3答案
#include<bits/stdc++.h>
using namespace std;
stack <char> s;
string x;
int main(){
cin>>x;
int len=x.size();
for(int i=0;i<len;i++){
if(x[i]=='('){
s.push(x[i]);
}
else{
if(s.empty()){
cout<<"0"<<endl;
return 0;
}
if(s.top()=='('){
s.pop();
}
else{
cout<<"0"<<endl;
return 0;
}
}
}
if(s.empty()){
cout<<"1"<<endl;
}else{
cout<<"0"<<endl;
}
}
/*
#include<bits/stdc++.h>
using namespace std;
int cnt=0;
string x;
int main(){
cin>>x;
int len=x.size();
for(int i=0;i<len;i++){
if(x[i]=='('){
cnt++;
}
else{
if(x[i]=='('){
cnt--;
}
}
}
if(cnt==0){
cout<<"1"<<endl;
}
else{
cout<<"0"<<endl;
}
}
题解:本题由于只需要处理一种括号(如果题目同时需要处理多种括号则不能采用本方法),因此栈中只可能出现“(” 和“) ”,因此我们甚至可以不用栈,只用一个数字来记录 没有被“)” 消掉的 “( ” 的数量即可。方法可以简化为,逐个读取符号,遇到“( ”则计数加一,遇到 “) ” 则计数减一,如果中间出现计数小于 0 的情况,则是不合法的。再读取完整个字符后,还需要判断计数是否为 0 ,不等于 0 则是不合法的。