栈
文章目录
声明
本篇是数据结构系列的第一章,主要内容是针对性的给出栈在实际题目中的用途和实现方式及其一般规律。
本系列基于代码源wls的数据结构系列课程,主要内容包括栈、队列、链表、树、堆、单调栈、单调队列、哈希、字典树、并查集持续更新,欢迎关注。
所有例题均可在Home - Daimayuan Online Judge的数据结构课程题单中提交。
栈
概念
栈就像一个顶端开口的容器,所有元素只能从上端进出。一个元素必须等待其上方所有元素全部取出时才能够被取出。
所以新进入的数据会在最上面,越先进入的数据在越下面
要素:
大小和栈顶指针top
栈的实现
用数组实现栈的基本操作
- push(x): 将元素x放到栈顶
- pop():将栈顶元素弹出
- top():查询栈顶元素
首先新建一个数组表示栈:
int s[10],top = 0;
push操作
s[++top] = x;
这句话相当于两句:
- top++
- s[top] = x;
意思就是先将栈顶指针top上移一位,再将元素x放到top所指的位置
pop操作
如果top > 0即栈非空,直接将top–即可,因为逻辑上栈从1开始(第一个元素放到s下标为1的地方)到top结束,top–即代表将栈顶元素删除,后续再放入元素时一定会伴随着赋值操作,所以不清除top原本指向的元素是完全合理的。
if(top > 0){
--top;
}
例1:栈
细节处理
思路非常简单,只需要读取操作类型,做出相应操作即可。
不过还是有一些细节需要注意
- 判断是哪种操作类型不必比较整个字符串,可以通过关键字符的比较判断是哪个操作
- 输入变量及数据结构定义为全局变量,这样逻辑清晰、全局可以使用、自动具有初始值
- cin,cout一般不用,太慢了,可能会被样例卡。所以对应的因为string类型一定要cin,所以一般使用char str[]来保存
- 最好少在循环里定义变量,多次分配空间可能会被卡
#include <bits/stdc++.h>
using namespace std;
int s[100001], top, m;
char str[11];
int main() {
scanf("%d", &m);
int a;
for (int i = 1; i <= m; ++i) {
scanf("%s", str);
if (str[1] == 'u') {//push
scanf("%d", &a);
s[++top] = a;
} else {
if (str[0] == 't') {//top
printf("%d\n", s[top]);
} else {//pop
top--;
}
}
}
return 0;
}
例2:栈2
算法分析
增加一种询问方式:
query(k):询问从顶向下数第k个元素是多少
这个操作在用数组模拟栈时非常好实现。
从上往下数第2个元素就是s[top - 1],第三个就是s[top - 2]……第k个就是s[top - k + 1]
所以query(k)就是s[top - k + 1]
所以逻辑跟上一个例题差不了太多,仅仅是把query的处理改变了一点点
细节处理
输出一定要换行
#include <bits/stdc++.h>
using namespace std;
int s[100001], top, m;
char str[11];
int main() {
scanf("%d", &m);
for (int i = 1; i <= m; ++i) {
scanf("%s", str);
if (str[2] == 's') {
int a;
scanf("%d", &a);
s[++top] = a;
} else {
if (str[0] == 'p') {
top--;
} else {
int k;
scanf("%d", &k);
printf("%d\n", s[top - k + 1]);
}
}
}
return 0;
}
例3:出栈序列判断
算法逻辑
如果栈顶元素不等于当前输出的元素x,就把比x小比栈顶元素大的整数都压入栈中,然后输出栈顶并pop
实例演示
例:1 3 5 4 2
首先栈顶元素0不等于1,将1压入栈,然后pop。
然后3不等于栈顶元素0,将2,3压入栈,然后pop
5不等于栈顶元素2,将4,5压入栈,然后pop
4等于栈顶元素,pop
2等于栈顶元素,pop
代码逻辑
用l记录上一个栈顶元素是几即可,顺便在push和pop的地方加上输出语句
#include <bits/stdc++.h>
using namespace std;
int s[100001], top, m;
int main() {
scanf("%d", &m);
int l = 0;
for (int i = 1; i <= m; ++i) {
int x;
scanf("%d", &x);
if (s[top] != x) {
for (int j = l + 1; j <= x; ++j) {
s[++top] = j;
printf("push %d\n", j);
}
l = x;
}
printf("pop\n");
--top;
}
return 0;
}
例4:括号序列
算法逻辑
逻辑并不复杂。
- 如果遇到左括号,放入栈中
- 如果遇到了右括号,看栈顶是否与其匹配,匹配就top–,不匹配就直接结束输出No
所以需要使用一个标志变量ok来标识是否匹配
细节处理
主要是栈是否为空的处理
- 首先如果读入的是右括号,需要判断栈是否为空,只有栈非空才能进行匹配
- 结束后判断栈是否为空,不为空则代表匹配失败
代码逻辑
直接实现上述逻辑即可
#include <bits/stdc++.h>
using namespace std;
int top, m;
char str[100001], s[100001];
int main() {
scanf("%d", &m);
scanf("%s", str + 1);
bool ok = true;
for (int i = 1; i <= m && ok; ++i) {
if (str[i] == '[' || str[i] == '(') {
s[++top] = str[i];
} else {
if (!top) {
ok = false;
} else {
if (str[i] == ')') {
if (s[top] == '(') {
top--;
} else {
ok = false;
}
} else {
if (s[top] == '[') {
top--;
} else {
ok = false;
}
}
}
}
}
if (top) {
ok = false;
}
if (ok) {
printf("Yes\n");
} else {
printf("No\n");
}
return 0;
}
例5:字符串处理
算法分析
算法逻辑也非常简单,跟上一题类似
当str[i] != s[top],即当前遍历到的元素不等于栈顶元素时,将其压入栈中,相等时将其从栈中弹出,
代码逻辑
按上述逻辑进行判断,最后输出栈内剩余元素即可。
#include <bits/stdc++.h>
using namespace std;
int top, n;
char str[100001], s[100001];
int main() {
scanf("%d", &n);
scanf("%s", str + 1);
for (int i = 1; i <= n; ++i) {
if (str[i] != s[top]) {
s[++top] = str[i];
} else {
top--;
}
}
for (int i = 1; i <= top; ++i) {
printf("%c", s[i]);
}
return 0;
}
stl用法
当然由于stl的栈比较慢而且栈用数组实现起来非常简单所以一般不使用stl。不过stl还是很香的
定义
stack<数据类型> 名字;
操作
基本操作只需要调用函数即可
stack<int> q;
- q.push(x):压入x
- q.pop():弹出栈顶元素
- q.top():返回栈顶元素
- q.size():返回栈内元素数量
- q.empty():返回栈是否为空
所以stl的栈用起来十分的便捷,只需要知道这些函数的作用即可