GESP 6级
stack 和 queue
6级 · 指针/STL/复杂度
💡 为什么重要
stack(栈)和 queue(队列)是两种最基本的受限数据结构。栈的"后进先出"特性是括号匹配、函数调用栈、表达式求值等问题的核心。队列的"先进先出"特性是BFS(广度优先搜索)的必备工具。GESP 6级考试几乎必考这两种容器的应用场景。
📖 前置知识
- vector 的基本操作(push_back、pop_back、size、empty)
- for 循环和 while 循环
- 字符串的基本处理
🔍 什么是栈(Stack)?
栈是一种后进先出(LIFO, Last In First Out)的数据结构。想象一摞盘子:你只能从最上面放盘子,也只能从最上面取盘子。最后放上去的盘子最先被取走。
操作只有三种:
- push:往栈顶放入一个元素
- pop:从栈顶取出一个元素
- top:查看栈顶元素(不取走)
📐 stack 操作
#include <stack>
stack<int> st; // 创建空栈
st.push(x) // 压入x到栈顶
st.pop() // 弹出栈顶元素(无返回值)
st.top() // 返回栈顶元素(不弹出)
st.size() // 元素个数
st.empty() // 是否为空
stack<int> st; // 创建空栈
st.push(x) // 压入x到栈顶
st.pop() // 弹出栈顶元素(无返回值)
st.top() // 返回栈顶元素(不弹出)
st.size() // 元素个数
st.empty() // 是否为空
💻 stack 代码详解
📝 示例1:stack 基本操作
#include <iostream>
#include <stack> // 必须包含这个头文件
using namespace std;
int main() {
stack<int> st; // 创建一个空栈
// 压入元素(像摞盘子)
st.push(10); // 栈: [10]
st.push(20); // 栈: [10, 20](20在栈顶)
st.push(30); // 栈: [10, 20, 30](30在栈顶)
// 查看栈顶(不取走)
cout << "栈顶: " << st.top() << endl;
// 输出: 栈顶: 30
// 弹出栈顶(取走盘子)
st.pop(); // 弹出30,栈变成 [10, 20]
cout << "弹出后栈顶: " << st.top() << endl;
// 输出: 弹出后栈顶: 20
// 查看大小
cout << "大小: " << st.size() << endl;
// 输出: 大小: 2
// 依次弹出所有元素(栈空时结束)
while (!st.empty()) {
cout << st.top() << " "; // 先看再弹
st.pop();
}
cout << endl; // 输出: 20 10(后进先出!)
return 0;
}
📝 示例2:经典应用——括号匹配
#include <iostream>
#include <stack>
#include <string>
using namespace std;
bool isBalanced(string s) {
stack<char> st; // 用栈来配对括号
for (char c : s) { // 遍历字符串中的每个字符
if (c == '(' || c == '[' || c == '{') {
st.push(c); // 遇到左括号,压入栈
} else {
// 遇到右括号
if (st.empty()) return false; // 栈空,没有左括号配对
char top = st.top(); // 查看栈顶
st.pop(); // 弹出栈顶
// 检查是否匹配
if (c == ')' && top != '(') return false;
if (c == ']' && top != '[') return false;
if (c == '}' && top != '{') return false;
}
}
return st.empty(); // 栈空=全部配对成功
}
int main() {
cout << isBalanced("()[]{}") << endl; // 1(true)
cout << isBalanced("([{}])") << endl; // 1(true)
cout << isBalanced("(]") << endl; // 0(false)
cout << isBalanced("(((") << endl; // 0(false)
return 0;
}
🔍 什么是队列(Queue)?
队列是一种先进先出(FIFO, First In First Out)的数据结构。想象排队买票:先来的人先买到票离开。
操作有四种:
- push:在队尾放入元素
- pop:从队头取走元素
- front:查看队头元素
- back:查看队尾元素
📐 queue 操作
#include <queue>
queue<int> q; // 创建空队列
q.push(x) // 在队尾加入x
q.pop() // 移除队头元素(无返回值)
q.front() // 返回队头元素
q.back() // 返回队尾元素
q.size() // 元素个数
q.empty() // 是否为空
queue<int> q; // 创建空队列
q.push(x) // 在队尾加入x
q.pop() // 移除队头元素(无返回值)
q.front() // 返回队头元素
q.back() // 返回队尾元素
q.size() // 元素个数
q.empty() // 是否为空
💻 queue 代码详解
📝 示例3:queue 基本操作
#include <iostream>
#include <queue>
using namespace std;
int main() {
queue<int> q; // 创建空队列
// 入队(像排队)
q.push(10); // 队列: [10]
q.push(20); // 队列: [10, 20](20在队尾)
q.push(30); // 队列: [10, 20, 30](30在队尾)
// 查看队头和队尾
cout << "队头: " << q.front() << endl; // 输出10
cout << "队尾: " << q.back() << endl; // 输出30
// 出队(先进先出)
q.pop(); // 弹出10,队列: [20, 30]
cout << "出队后队头: " << q.front() << endl;
// 输出: 出队后队头: 20
// 依次出队
while (!q.empty()) {
cout << q.front() << " "; // 先看再弹
q.pop();
}
cout << endl; // 输出: 20 30(先进先出!)
return 0;
}
📝 示例4:经典应用——BFS(广度优先搜索)
#include <iostream>
#include <queue>
using namespace std;
// 简单的BFS示例:从节点1开始,逐层访问
// 图的邻接表表示
vector<int> adj[6]; // 6个节点的图(编号1~5)
int main() {
// 建图(无向图)
adj[1] = {2, 3}; // 1连接2和3
adj[2] = {1, 4}; // 2连接1和4
adj[3] = {1, 5}; // 3连接1和5
adj[4] = {2}; // 4连接2
adj[5] = {3}; // 5连接3
bool visited[6] = {false}; // 记录是否访问过
queue<int> q; // BFS用的队列
// 从节点1开始BFS
q.push(1); // 起点入队
visited[1] = true; // 标记为已访问
while (!q.empty()) {
int u = q.front(); // 取出队头
q.pop(); // 弹出队头
cout << u << " "; // 访问节点u
// 把u的所有未访问邻居入队
for (int v : adj[u]) {
if (!visited[v]) {
visited[v] = true; // 标记(避免重复访问)
q.push(v); // 入队
}
}
}
cout << endl;
// 输出: 1 2 3 4 5(逐层访问)
return 0;
}
⚠️ 易错点
- pop() 没有返回值:
st.pop()和q.pop()都不返回值!要先top()或front()获取值,再 pop - 对空栈/空队列操作:对空的 stack 调 top()、对空的 queue 调 front() 都是未定义行为。先用 empty() 检查
- 栈和队列没有下标访问:不能用
st[0]或q[0],只能访问栈顶或队头 - BFS 中忘记标记 visited:如果不标记已访问,队列中可能反复放入同一个节点,导致死循环
- 括号匹配忘记检查栈空:遇到右括号时,如果栈已空(没有左括号匹配),应该返回 false
🎯 练习建议
- 入门:用 stack 将一个整数按位拆分并倒序输出(如 1234 → 4 3 2 1)
- 进阶:实现完整的括号匹配(支持三种括号:()、[]、{})
- 挑战:用两个栈实现一个队列
- BFS 练习:在网格迷宫中,用 BFS 找到从起点到终点的最短路径
- 思考:为什么 BFS 用队列而 DFS 用栈(或递归)?