概括:
栈和队列都是操作受限制的线性表。不可以随便读取中间的数据
3.1 栈(后进先出 LIFO)
栈(stack)是只允许在一段进行插入/删除的线性表
栈顶(top):允许进行插入/删除的一端
数学性质:n个元素进展,出栈的排列个数为卡特兰数
3.1.2 顺序栈: 栈的顺序存储实现
采用一组连续的地址存放栈底到栈顶的元素, 用一个指针top指向当前的栈顶元素,top初始为-1
常用操作:
- 栈空:
s.top == -1
- 栈满:
s.top == MAXSIZE - 1
- 入栈:
s[++top] = i
- 出栈:
top--
缺点: 栈的长度受数组上约束,栈可能会上溢
共享栈:
3.1.2 链栈: 栈的链式存储实现
用单链表实现,规定所有的操作均在表头进行,规定链栈没有头结点,Lhead指向栈顶元素
优点: 栈不会上溢
错题笔记:
- 栈和队列都有相同的:
逻辑结构
(都是线性结构) - 向一个栈顶指针为top的链栈(不带头结点)中插入一个x结点,应执行:
x->next=top; top = x
- 设链表不带头结点且所有操作都在表头进行,下面不适合作为链栈的是:
只有表头结点指针,没有表尾指针的单向循环链表
,因为此时要找到指向表头结点的指针需要遍历整个链表 - 错误:
采用非递归方式重写递归必须用到栈
,例如斐波那契非递归只需要一个循环 - 【2013统考】一个栈的入栈序列为1、2、3……n,出栈序列是p1、p2、p3……pn,如果p2=3,则p3的可能取值为
n-1
3.2 队列(后进先出 FIFO)
允许在一端(队尾)进行插入,在另一端(队头)进行删除
3.2.1 队列的顺序实现
分配一块连续的存储单元来存放队列中的元素,一般设置两个指针:
- front队头指针指向队头元素
- rear队尾指针指向队尾元素的下一个位置
3.3 栈和队列的应用
栈的应用:
- 括号匹配
- 表达式求值
- 递归
- 迷宫求解
括号匹配:
输入一行由 <
、(
、{
、[
、>
、)
、}
、]
构成的字符串
如果输入的字符串中的括号正确匹配则输出 yes,否则输出 no。
例如输入:(){}
,输出yes
思路:
循环扫描字符串,遇到左括号就讲它入栈,遇到右括号就检查是否栈不为空且能和栈顶符号匹配
#include <iostream>
#include <stack>
#include <cstring>
using namespace std;
stack<char> stk;
string str;
int main(){
cin >> str;
bool flag = true;
for(int i = 0; i < str.size(); i ++ ){
char op = str[i];
// 当前是左括号就入栈
if(op == '<' || op == '(' || op == '{' || op == '[') stk.push(op);
else{
if(stk.empty()){
flag = false;
break;
}
if(op == '>' && stk.top() == '<') stk.pop();
else if(op == ')' && stk.top() == '(') stk.pop();
else if(op == '}' && stk.top() == '{') stk.pop();
else if(op == ']' && stk.top() == '[') stk.pop();
else{
flag = false;
break;
}
}
}
// 最后栈不为空,也非法
if(!stk.empty()) flag = false;
if(flag == true) cout << "yes" << endl;
else cout << "no" << endl;
return 0;
}
队列的应用:
- 树的层次遍历
- 图的广度优先搜索(bfs)
- 计算机系统的缓冲区