C++ STL 容器适配器详解
——栈、队列、优先队列的使用与底层原理
📌 什么是容器适配器(Container Adapters)?
在 C++ STL 中,容器适配器不是独立的数据结构,而是基于已有容器(如 deque、vector、list)封装出的新接口,以提供特定的访问逻辑(如 LIFO、FIFO、按优先级访问)。
STL 提供了三种标准容器适配器:
| 适配器 | 行为 | 默认底层容器 |
|---|---|---|
stack | 后进先出(LIFO) | deque |
queue | 先进先出(FIFO) | deque |
priority_queue | 按优先级出队(最大堆/最小堆) | vector |
✅ 关键特点:
- 不支持迭代器(不能遍历)
- 只能通过特定接口访问元素(如
top()、push()、pop())- 可自定义底层容器(需满足特定要求)
1️⃣ stack(栈)
基本操作
#include <stack>
using namespace std;
stack<int> s;
s.push(10); // 入栈
s.push(20);
cout << s.top(); // 访问栈顶 → 20
s.pop(); // 弹出栈顶(无返回值)
cout << s.size(); // 元素个数
cout << s.empty(); // 是否为空
自定义底层容器
// 使用 vector 作为底层
stack<int, vector<int>> s1;
// 使用 list 作为底层(较少见)
stack<int, list<int>> s2;
⚠️ 要求:底层容器必须支持
back(),push_back(),pop_back()—— 所以vector、deque、list都可以。
2️⃣ queue(队列)
基本操作
#include <queue>
queue<int> q;
q.push(1); // 入队
q.push(2);
cout << q.front(); // 队首 → 1
cout << q.back(); // 队尾 → 2
q.pop(); // 出队(无返回值)
自定义底层容器
// 必须使用支持 push_back 和 pop_front 的容器
queue<int, deque<int>> q1; // 默认
queue<int, list<int>> q2; // list 也支持
// queue<int, vector<int>> ❌ 错误!vector 没有 pop_front()
3️⃣ priority_queue(优先队列)
本质是一个堆(heap) ,默认是最大堆(最大的元素在顶部)。
基本用法(最大堆)
priority_queue<int> pq; // 默认:最大堆
pq.push(10);
pq.push(30);
pq.push(20);
cout << pq.top(); // → 30(最大值)
pq.pop(); // 移除30
最小堆实现
// 方法1:使用 greater<int>
priority_queue<int, vector<int>, greater<int>> min_pq;
// 方法2:存负数(不推荐)
priority_queue<int> pq;
pq.push(-10); pq.push(-20);
cout << -pq.top(); // 得到最小正数
自定义结构体的优先级
struct Person {
string name;
int age;
};
// 方式1:重载 < 运算符(注意:最大堆,所以“年龄大”的优先级高)
bool operator<(const Person& a, const Person& b) {
return a.age < b.age; // 注意:这里 < 表示 a 优先级低于 b
}
// 方式2:使用仿函数(更灵活)
struct CompareAge {
bool operator()(const Person& a, const Person& b) {
return a.age > b.age; // 注意:这里 > 表示 a 优先级更低(用于最小堆)
}
};
priority_queue<Person, vector<Person>, CompareAge> pq;
🔍 重要提示:
priority_queue的比较函数逻辑是:
- 如果
comp(a, b) == true,则 b 的优先级高于 a- 所以最小堆用
greater<T>(即a > b时 b 更小,应优先)
🔧 底层容器选择建议
| 适配器 | 推荐底层容器 | 原因 |
|---|---|---|
stack | deque(默认)或 vector | vector 内存连续,deque 扩容更高效 |
queue | deque(默认) | 支持两端高效操作,list 也可但内存开销大 |
priority_queue | vector(默认) | 堆通常用数组实现,vector 最合适 |
⚠️ 常见误区
-
pop()不返回值int x = s.pop(); // ❌ 错误!pop() 返回 void // 正确做法: int x = s.top(); s.pop(); -
priority_queue是最大堆,不是最小堆
想要最小堆必须显式指定greater<T>。 -
不能遍历容器适配器
没有begin()/end(),无法用 for-each 或算法库。 -
底层容器不可直接访问
适配器封装了底层容器,你无法拿到内部的deque或vector。
✅ 总结:何时使用哪个适配器?
| 场景 | 选择 |
|---|---|
| 函数调用、括号匹配、表达式求值 | stack |
| BFS、任务调度、缓冲区 | queue |
| Top-K 问题、Dijkstra 算法、任务按优先级处理 | priority_queue |
💡 记住:容器适配器是“接口包装器”,它们让已有容器表现出新的行为,复用而不重复造轮子,这正是 STL 设计哲学的体现!
掌握这三类适配器,你就能高效解决大多数需要栈、队列或堆的算法问题!