C++ 容器详解:stack、queue、deque 基本用法与对比
在 C++ STL 中,std::stack、std::queue 和 std::deque 是非常常用的容器,但它们定位和能力有明显区别。
stack和queue是容器适配器(container adapter),它们本身不存储数据,而是基于底层容器(默认是deque)提供特定接口限制。deque是真正的序列容器,功能最强大,支持两端高效操作。
下面从使用方式、接口、底层实现、适用场景进行详细对比和代码演示。
1. 三者快速对比表
| 特性 | std::stack | std::queue | std::deque |
|---|---|---|---|
| 数据结构语义 | LIFO(后进先出)栈 | FIFO(先进先出)队列 | 双端队列(Double-ended queue) |
| 默认底层容器 | deque | deque | —(本身就是容器) |
| 可自定义底层容器 | 是(deque/list/vector) | 是(deque/list) | — |
| 两端操作 | 仅顶端 | 入尾、出头 | 两端都高效 |
| 随机访问 [] / at() | 不支持 | 不支持 | 支持(O(1)) |
| 迭代器遍历 | 不支持 | 不支持 | 支持(随机访问迭代器) |
| 典型场景 | 表达式求值、DFS、函数调用栈 | BFS、任务队列、打印队列 | 需要两端操作 + 随机访问的场景 |
| 头文件 | <stack> | <queue> | <deque> |
2. std::deque —— 最灵活的双端队列
deque 是真正的基础容器,支持:
- 两端高效插入/删除(O(1) 均摊)
- 随机访问(O(1))
- 中间插入/删除较慢(O(n))
基本用法示例
#include <iostream>
#include <deque>
#include <string>
int main() {
std::deque<int> dq;
// 两端插入
dq.push_back(10); // 尾部插入
dq.push_back(20);
dq.push_front(5); // 头部插入
dq.push_front(1);
// 当前内容:1 5 10 20
std::cout << "front = " << dq.front() << ", back = " << dq.back() << "\n";
// 随机访问
std::cout << "第2个元素: " << dq[1] << "\n"; // 5
std::cout << "第3个元素: " << dq.at(2) << "\n"; // 10(带边界检查)
// 遍历(支持范围for、迭代器)
std::cout << "遍历: ";
for (int x : dq) std::cout << x << " ";
std::cout << "\n";
// 两端删除
dq.pop_front(); // 删除 1
dq.pop_back(); // 删除 20
// 清空判断
while (!dq.empty()) {
std::cout << "弹出: " << dq.front() << "\n";
dq.pop_front();
}
return 0;
}
deque 常用成员函数
push_back(),pop_back()push_front(),pop_front()front(),back()operator[],at()size(),empty(),clear()resize(),reserve()(效果不如 vector 明显)
3. std::stack —— 经典栈(LIFO)
接口非常精简,只允许从栈顶操作。
基本用法
#include <iostream>
#include <stack>
int main() {
std::stack<std::string> s;
// 压栈
s.push("main");
s.push("funcA");
s.push("funcB");
std::cout << "栈大小: " << s.size() << "\n"; // 3
// 访问栈顶(不弹出)
std::cout << "当前栈顶: " << s.top() << "\n"; // funcB
// 出栈
s.pop(); // 移除 funcB
std::cout << "出栈后栈顶: " << s.top() << "\n"; // funcA
// 清空栈
while (!s.empty()) {
std::cout << "弹出: " << s.top() << "\n";
s.pop();
}
return 0;
}
注意:stack 没有 clear(),也没有迭代器,不能遍历。
自定义底层容器(很少用)
std::stack<int, std::vector<int>> vec_stack; // 用 vector 实现栈
std::stack<int, std::list<int>> list_stack; // 用 list 实现栈
4. std::queue —— 经典队列(FIFO)
只允许队尾入、队首出。
基本用法
#include <iostream>
#include <queue>
int main() {
std::queue<int> q;
// 入队
q.push(100);
q.push(200);
q.push(300);
std::cout << "队首: " << q.front() << "\n"; // 100
std::cout << "队尾: " << q.back() << "\n"; // 300
// 出队
q.pop(); // 移除 100
std::cout << "出队后队首: " << q.front() << "\n"; // 200
// 遍历清空
std::cout << "队列元素: ";
while (!q.empty()) {
std::cout << q.front() << " ";
q.pop();
}
std::cout << "\n";
return 0;
}
注意:同样没有迭代器、没有 clear(),只能从两端访问 front/back。
自定义底层容器
std::queue<int, std::list<int>> list_queue; // 用 list 实现队列(较少用)
5. 选择建议总结(实际工程中怎么选)
| 场景 | 推荐容器 | 理由 |
|---|---|---|
| 只需要经典栈(括号匹配、DFS、单调栈) | std::stack | 接口最清晰,表达意图最强 |
| 只需要经典队列(BFS、任务队列) | std::queue | 接口最清晰,防止误用 |
| 需要两端都频繁插入/删除 | std::deque | 两端 O(1),且支持随机访问 |
| 需要两端操作 + 偶尔随机访问 | std::deque | 比 vector 更适合频繁头插 |
| 追求极致尾部性能 + 内存连续 | std::vector | 尾部最快,缓存友好,但头插很慢 |
| 频繁任意位置插入/删除 | std::list | O(1) 任意位置插入,但无随机访问 |
最常见的选择路径:
- 简单栈 →
stack - 简单队列 →
queue - 需要两端高效操作,或不确定未来需求 → 直接用
deque - 追求尾部极致性能 + 内存连续 →
vector
希望这篇详解能帮你清晰地区分和正确使用这三个容器!
如果想看更具体的应用场景(单调栈、滑动窗口、双端 BFS 等),可以继续告诉我。