
个人主页 : 流年如梦
专栏 : 《零基础轻松入门C语言》 《数据结构:从入门到掌握》 《C++编程研习录》
文章目录
- 一.stack的介绍和使用
-
- [1.1 stack的介绍](#1.1 stack的介绍)
- [1.2 stack的常用接口](#1.2 stack的常用接口)
-
- [1.2.1 最小栈](#1.2.1 最小栈)
- [1.2.2 栈的弹出序列](#1.2.2 栈的弹出序列)
- [1.2.3 逆波兰表达式求值](#1.2.3 逆波兰表达式求值)
- [1.3 stack 的模拟实现](#1.3 stack 的模拟实现)
- [二.queue 的介绍和使用](#二.queue 的介绍和使用)
-
- [2.1 queue的介绍](#2.1 queue的介绍)
- [2.2 queue的常用接口](#2.2 queue的常用接口)
- [2.3 queue的模拟实现](#2.3 queue的模拟实现)
- 三.priority_queue的介绍和使用
-
- [3.1 priority_queue的介绍](#3.1 priority_queue的介绍)
- [3.2 priority_queue的常用接口](#3.2 priority_queue的常用接口)
- [3.3 OJ 实战:数组中第 K 个最大元素](#3.3 OJ 实战:数组中第 K 个最大元素)
- [3.4 priority_queue的模拟实现](#3.4 priority_queue的模拟实现)
- 四.容器适配器与deque
-
- [4.1 什么是适配器模式](#4.1 什么是适配器模式)
- [4.2 STL 适配器的模板定义](#4.2 STL 适配器的模板定义)
- [4.3 deque简单介绍](#4.3 deque简单介绍)
- [4.4 为什么选择deque作为默认底层](#4.4 为什么选择deque作为默认底层)
- [4.5 标准库式模拟实现(可指定底层容器)](#4.5 标准库式模拟实现(可指定底层容器))
- 🎯总结
- ⚠️易错点
前言 stack、queue、priority_queue 是 STL 中三类典型的**容器适配器**。它们并不直接实现底层数据结构,而是对已有容器进行封装,对外提供语义明确的操作接口。其中,stack 遵循后进先出(LIFO)原则,仅允许在栈顶进行操作;queue 遵循先进先出(FIFO)原则,队尾入队、队头出队;priority_queue 则基于堆结构,保证堆顶元素始终拥有最高优先级。本文将从接口使用与经典算法场景入手,系统讲解这三类适配器,并逐步完成它们的模拟实现,深入剖析容器适配器设计模式与底层 deque 的原理,同时对比不同底层容器的优劣。
一.stack的介绍和使用
1.1 stack的介绍
stack 是**后进先出(Last In First Out)**的容器适配器,仅支持栈顶一端的插入、删除与访问,不支持遍历与随机访问。默认以 deque 作为底层容器,也可指定 vector、list 作为底层实现
典型应用场景:括号匹配、表达式求值、函数调用栈、模拟递归等
1.2 stack的常用接口
使用 stack 必须包含头文件:#include <stack>
| 接口 | 说明 |
|---|---|
| stack() | 构造空栈 |
| empty() | 检测栈是否为空,空返回 true |
| size() | 返回栈中元素个数 |
| top() | 返回栈顶元素的引用 |
| push() | 将元素压入栈顶 |
| pop() | 弹出栈顶元素 |
1.2.1 最小栈
题目:

思路:双栈实现,元素栈存数据,辅助栈存每一步的最小值,两个栈同步操作,保证getMin时间复杂度 O (1)
代码:
cpp
class MinStack {
public:
void push(int x) {
_elem.push(x);
// 空栈或新元素更小,则压入辅助栈
if(_min.empty() || x <= _min.top())
_min.push(x);
}
void pop() {
// 弹出元素等于当前最小值,辅助栈同步弹出
if(_min.top() == _elem.top())
_min.pop();
_elem.pop();
}
int top() { return _elem.top(); }
int getMin() { return _min.top(); }
private:
stack<int> _elem; // 存储所有元素
stack<int> _min; // 维护最小值序列
};
1.2.2 栈的弹出序列
题目:

思路:用辅助栈模拟压入弹出过程,按弹出序列匹配栈顶,匹配成功则弹出,全部匹配完成则合法
代码:
cpp
class Solution {
public:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
if(pushed.size() != popped.size())
return false;
stack<int> st;
int inIdx = 0, outIdx = 0;
while(outIdx < popped.size()) {
// 栈空或栈顶不匹配,持续压栈
while(st.empty() || st.top() != popped[outIdx]) {
if(inIdx >= pushed.size())
return false;
st.push(pushed[inIdx++]);
}
// 栈顶匹配则弹出
st.pop();
outIdx++;
}
return true;
}
};
1.2.3 逆波兰表达式求值
题目:

思路:遇到数字压栈,遇到运算符弹出两个数计算,结果重新压栈,最终栈顶即为表达式结果。注意操作数顺序,先弹出的是右操作数
代码:
cpp
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> st;
for(auto& str : tokens) {
// 数字直接入栈
if(str != "+" && str != "-" && str != "*" && str != "/") {
st.push(stoi(str));
}
else {
// 先弹出右操作数,再弹出左操作数
int right = st.top(); st.pop();
int left = st.top(); st.pop();
switch(str[0]) {
case '+': st.push(left + right); break;
case '-': st.push(left - right); break;
case '*': st.push(left * right); break;
case '/': st.push(left / right); break;
}
}
}
return st.top();
}
};
1.3 stack 的模拟实现
栈仅需尾插尾删,使用 vector 封装即可实现
cpp
#include <vector>
namespace my
{
template<class T>
class stack {
public:
stack() {}
void push(const T& x) { _c.push_back(x); }
void pop() { _c.pop_back(); }
T& top() { return _c.back(); }
const T& top() const { return _c.back(); }
size_t size() const { return _c.size(); }
bool empty() const { return _c.empty(); }
private:
vector<T> _c; // 底层容器
};
}
二.queue 的介绍和使用
2.1 queue的介绍
queue 是**先进先出(First In First Out)**的容器适配器,支持队尾插入、队头删除,不支持随机访问与遍历。默认以 deque 作为底层容器,也可指定 list 作为底层实现
典型应用场景:广度优先搜索(BFS)、任务队列、消息队列、缓存等
2.2 queue的常用接口
使用 queue 必须包含头文件:#include <queue>
| 接口 | 说明 |
|---|---|
| queue() | 构造空队列 |
| empty() | 检测队列是否为空,空返回 true |
| size() | 返回队列中元素个数 |
| front() | 返回队头元素的引用 |
| back() | 返回队尾元素的引用 |
| push() | 队尾插入元素 |
| pop() | 队头弹出元素 |
2.3 queue的模拟实现
队列需要尾插头删,vector 头删效率为 O (N),因此选用 list 封装实现
cpp
#include <list>
namespace my
{
template<class T>
class queue {
public:
queue() {}
void push(const T& x) { _c.push_back(x); }
void pop() { _c.pop_front(); }
T& front() { return _c.front(); }
const T& front() const { return _c.front(); }
T& back() { return _c.back(); }
const T& back() const { return _c.back(); }
size_t size() const { return _c.size(); }
bool empty() const { return _c.empty(); }
private:
list<T> _c; // 底层容器
};
}
三.priority_queue的介绍和使用
3.1 priority_queue的介绍
priority_queue 又称优先级队列,是基于堆结构 的容器适配器,保证队顶元素始终是优先级最高的元素。默认以 vector 作为底层容器,默认构建大堆(顶部元素最大)
典型应用场景:TopK 问题、任务优先级调度、合并有序链表等
3.2 priority_queue的常用接口
使用 priority_queue 必须包含头文件:#include <queue>,使用 greater 比较器需包含 #include <functional>
| 接口 | 说明 |
|---|---|
| priority_queue() | 构造空优先级队列 |
| priority_queue(first, last) | 用迭代器区间构造 |
| empty() | 检测是否为空 |
| top() | 返回堆顶(优先级最高)元素 |
| push(x) | 插入元素并调整堆结构 |
| pop() | 删除堆顶元素并调整堆 |
注意事项
- 默认是大堆,比较器为
less<T>;创建小堆需指定第三个模板参数greater<T> - 自定义类型放入优先级队列,必须重载
<(大堆)或>(小堆)运算符
举例:
cpp
// 默认大堆,顶部元素最大
priority_queue<int> q1;
// 小堆,顶部元素最小
priority_queue<int, vector<int>, greater<int>> q2;
3.3 OJ 实战:数组中第 K 个最大元素
题目:

思路:利用大顶堆特性,所有元素入堆后,依次弹出 k-1 个最大值,堆顶即为第 k 大元素
代码:
cpp
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
priority_queue<int> pq(nums.begin(), nums.end());
// 弹出前k-1个最大元素
for(int i = 0; i < k - 1; ++i)
pq.pop();
return pq.top();
}
};
3.4 priority_queue的模拟实现
底层基于 vector 存储,封装堆调整算法实现优先级队列
cpp
#include <vector>
#include <algorithm>
namespace my
{
template<class T, class Container = vector<T>, class Compare = less<T>>
class priority_queue {
public:
priority_queue() {}
template<class InputIterator>
priority_queue(InputIterator first, InputIterator last)
: _c(first, last)
{
make_heap(_c.begin(), _c.end(), _cmp);
}
void push(const T& x) {
_c.push_back(x);
push_heap(_c.begin(), _c.end(), _cmp);
}
void pop() {
pop_heap(_c.begin(), _c.end(), _cmp);
_c.pop_back();
}
const T& top() const { return _c.front(); }
size_t size() const { return _c.size(); }
bool empty() const { return _c.empty(); }
private:
Container _c;
Compare _cmp;
};
}
四.容器适配器与deque
4.1 什么是适配器模式
适配器是一种结构型设计模式,核心作用是将一个类的接口转换成客户期望的另一种接口,让原本接口不兼容的类可以协同工作。STL 容器适配器就是封装底层容器,对外提供统一的特定语义接口,屏蔽底层容器的差异
本质是接口转换与封装,属于黑箱复用,耦合度低于继承
4.2 STL 适配器的模板定义
stack 和 queue 第二个模板参数指定底层容器,默认均为 deque:
cpp
template<class T, class Container = deque<T>> class stack;
template<class T, class Container = deque<T>> class queue;
priority_queue 默认底层为 vector,比较器默认 less:
cpp
template<class T, class Container = vector<T>, class Compare = less<T>> class priority_queue;
4.3 deque简单介绍
deque(双端队列)是支持两端 O (1) 插入删除的序列容器,底层是分段连续的内存块,通过中控器管理多个缓冲区,对外模拟连续空间的假象。
- 优点:头尾插入删除 O (1),扩容无需大量移动元素,空间利用率高于 list
- 缺点:迭代器设计复杂,遍历效率低,随机访问性能弱于 vector
4.4 为什么选择deque作为默认底层
stack 和 queue 不需要遍历、不需要随机访问,仅在一端或两端操作,完美避开 deque 遍历效率低的缺点;同时 deque 头尾操作效率高,扩容成本低于 vector,空间利用率优于 list,因此成为栈与队列的默认底层容器
4.5 标准库式模拟实现(可指定底层容器)
cpp
// stack 通用版本,支持自定义底层容器
template<class T, class Con = deque<T>>
class stack {
public:
void push(const T& x) { _c.push_back(x); }
void pop() { _c.pop_back(); }
T& top() { return _c.back(); }
size_t size() const { return _c.size(); }
bool empty() const { return _c.empty(); }
private:
Con _c;
};
// queue 通用版本,支持自定义底层容器
template<class T, class Con = deque<T>>
class queue {
public:
void push(const T& x) { _c.push_back(x); }
void pop() { _c.pop_front(); }
T& front() { return _c.front(); }
T& back() { return _c.back(); }
size_t size() const { return _c.size(); }
bool empty() const { return _c.empty(); }
private:
Con _c;
};
🎯总结
- stack 是后进先出适配器,仅栈顶操作,默认底层 deque,适用于括号匹配、表达式求值等场景
- queue 是先进先出适配器,队尾入队头出,默认底层 deque,适用于 BFS、任务队列等场景
- priority_queue 是堆结构适配器,默认大堆,底层 vector,适用于 TopK、优先级调度等场景
- 容器适配器本质是接口封装,属于适配器设计模式,本身不实现原生数据结构
- deque 是分段连续双端队列,头尾操作高效,遍历性能弱,完美适配栈与队列的操作特性
- 最小栈、弹出序列验证、逆波兰求值、TopK 是栈与优先级队列的经典算法应用
- 适配器支持自定义底层容器,可根据场景选择 vector、list、deque 实现
⚠️易错点
- 混淆 stack 和 queue 的进出方向,栈尾进尾出,队列尾进头出
- priority_queue 默认是大堆,误以为是小堆导致 TopK 结果错误
- 自定义类型放入 priority_queue,未重载比较运算符导致编译错误
- 误以为 stack 和 queue 是容器,实际是容器适配器
- 用 vector 实现 queue,头删操作 O (N) 效率极低
- deque 随机访问效率低于 vector,遍历场景优先用 vector
- 逆波兰表达式求值时,操作数左右顺序搞反,减法除法结果出错
- 最小栈中相等元素不同时压入辅助栈,pop 后最小值错误
- priority_queue 底层是堆,插入删除都需要调整堆结构,时间复杂度 O (logN)
- 适配器底层容器不支持对应操作时编译报错,比如用 vector 做 queue 底层会因
pop_front失效
👀 关注 我们一路同行,从入门到大师,慢慢沉淀、稳步成长
❤️ 点赞 鼓励原创,让优质内容被更多人看见
⭐ 收藏 收好核心知识点与实战技巧,需要时随时查阅
💬 评论 分享你的疑问或踩坑经历,一起交流避坑、共同进步