一、前言:为什么需要 Stack 和 Queue?
在 C++ 的 STL(Standard Template Library)中,vector 、list 、deque 等容器提供了灵活的数据存取方式,但有时我们只需要**"只进不出"(Stack)或"只进只出"**(Queue)的数据结构来简化代码逻辑。
- Stack(栈):遵循**后进先出(LIFO)**原则。想象一摞盘子,最后放上去的盘子最先拿下来。
- Queue(队列):遵循**先进先出(FIFO)**原则。想象排队买票,最先到的人最先买到票。
这两者都是基于 容器适配器(Container Adaptor) 实现的,即它们封装了底层容器(通常是 deque 或 vector),只暴露有限的接口以满足特定的使用场景[[1]][[2]][[3]]。
二、Stack(栈)的详细介绍
1. 栈的特性与底层实现
- 特性:只允许在**栈顶(Top)**进行插入和删除操作。
- 底层容器 :默认使用
deque,但也可以显式指定为vector或list(只要底层容器支持back(),push_back(),pop_back())[[4]][[5]][[6]]。
2. 常用成员函数
cpp
stack<int> s; // 声明一个存放int类型的栈
s.empty(); // 判断栈是否为空,返回bool
s.size(); // 返回栈中元素的个数,返回size_t
s.top(); // 访问栈顶元素,返回对栈顶元素的引用
s.push(val); // 将元素压入栈顶
s.pop(); // 弹出栈顶元素(注意:pop()不返回值,仅删除)
s.emplace(args...); // 直接在栈顶构造元素(C++11)
注意 :栈不支持遍历(没有迭代器),只能通过
top()和pop()访问元素[[7]][[8]][[9]]。
3. 典型应用场景
- 表达式求值:中缀表达式转后缀表达式(逆波兰记法)[[10]]。
- 递归模拟:手动管理函数调用栈,防止栈溢出。
- 撤销功能:如编辑器的"撤销"操作,最近的操作最先撤销。
三、Queue(队列)的详细介绍
1. 队列的特性与底层实现
- 特性 :只能在**队尾(Back)插入,在队头(Front)**删除。
- 底层容器 :默认使用
deque,因为deque支持在两端高效插入删除[[11]][[12]][[13]]。
2. 常用成员函数
cpp
queue<int> q; // 声明一个存放int类型的队列
q.empty(); // 判断队列是否为空,返回bool
q.size(); // 返回队列中元素的个数,返回size_t
q.front(); // 访问队首元素,返回对队首元素的引用
q.back(); // 访问队尾元素,返回对队尾元素的引用
q.push(val); // 将元素加入队尾
q.pop(); // 弹出队首元素(注意:pop()不返回值,仅删除)
q.emplace(args...); // 直接在队尾构造元素(C++11)
注意 :和栈一样,队列也不支持遍历(没有迭代器),只能通过
front()和pop()访问元素[[14]][[15]][[16]]。
3. 典型应用场景
- 广度优先搜索(BFS):在图论算法中,使用队列来层层遍历节点[[17]]。
- 任务调度:如操作系统的进程调度,最先到达的任务最先执行。
- 缓存实现:如 LRU 缓存策略中的队列管理。
四、Stack 与 Queue 的对比
| 维度 | Stack (栈) | Queue (队列) |
|---|---|---|
| 原则 | LIFO(后进先出) | FIFO(先进先出) |
| 主要操作 | push, pop, top |
push, pop, front, back |
| 使用场景 | 递归、撤销、表达式求值 | BFS、任务调度、缓存 |
| 底层容器 | 默认 deque,可选 vector/list |
默认 deque |
| 是否支持遍历 | 否 | 否 |
五、实战代码演示
以下代码展示了 stack 和 queue 的基本使用方法,适合放在 CSDN 博客中作为示例代码块。
1. Stack 示例:整数入栈与出栈
cpp
#include <iostream>
#include <stack>
using namespace std;
int main() {
stack<int> s; // 声明一个栈
// 入栈操作
for (int i = 0; i < 5; ++i) {
s.push(i);
cout << "压入: " << i << endl;
}
// 出栈操作
while (!s.empty()) {
cout << "弹出: " << s.top() << endl;
s.pop();
}
return 0;
}
运行结果:
压入: 0
压入: 1
压入: 2
压入: 3
压入: 4
弹出: 4
弹出: 3
弹出: 2
弹出: 1
弹出: 0
2. Queue 示例:整数入队与出队
cpp
#include <iostream>
#include <queue>
using namespace std;
int main() {
queue<int> q; // 声明一个队列
// 入队操作
for (int i = 0; i < 5; ++i) {
q.push(i);
cout << "入队: " << i << endl;
}
// 出队操作
while (!q.empty()) {
cout << "出队: " << q.front() << endl;
q.pop();
}
return 0;
}
运行结果:
入队: 0
入队: 1
入队: 2
入队: 3
入队: 4
出队: 0
出队: 1
出队: 2
出队: 3
出队: 4
六、总结
stack 和 queue 虽然功能单一,但正因为它们的单向操作特性,使得代码更加简洁且效率更高。在日常开发中,了解它们的底层原理(如为什么默认使用 deque)以及它们的适用场景,可以帮助你写出更高效、更符合需求的代码。
通过本篇博客的介绍,你现在应该能熟练掌握这两个容器的基本用法,并能在合适的地方应用它们来解决实际问题了。[[18]][[19]][[20]]