📚 吃透 C++ STL 栈和队列
1 🥞 栈(stack):先进后出的 "叠盘子"
2 🚶 队列(queue):先进先出的 "排队买奶茶"
3 🏆 优先队列(priority_queue)
4 🧩 容器适配器:"套壳" 的底层逻辑
5 📝 新手避坑 & 复习总结
1 🥞 栈(stack):先进后出的 "叠盘子"
1.1 新手理解:啥是栈?
想象你往桌子上叠盘子:先放的盘子在最下面,后放的在最上面;拿的时候只能先拿最上面的,这就是后进先出(LIFO) ------ 栈的核心逻辑。
C++ 里的 stack 不是 "原生容器",而是 "容器适配器"(后面会讲),默认用 deque 当底层存储,新手不用纠结底层,先会用接口就行!
1.2 必背接口(记这 6 个就够)

💡 避坑:
- 别直接用top()!如果栈空,调用top()会直接崩溃,一定要先判断!s.empty();
- pop()只删元素,不返回值!想拿栈顶元素要先top()再pop()。
1.3 代码示例
cpp
#include <iostream>
#include <stack> // 必须包含头文件!
using namespace std;
int main() {
stack<int> s;
// 压栈(叠盘子)
s.push(1);
s.push(2);
s.push(3);
cout << "栈的大小:" << s.size() << endl; // 输出3
cout << "栈顶元素:" << s.top() << endl; // 输出3
// 出栈(拿盘子)
s.pop();
cout << "出栈后栈顶:" << s.top() << endl; // 输出2
// 遍历栈(新手注意:栈没有迭代器,只能边删边看)
while (!s.empty()) {
cout << s.top() << " "; // 输出2 1
s.pop();
}
return 0;
}
1.4 经典刷题场景(新手版解析)
(1)最小栈(面试高频)
**问题:**实现一个栈,能在 O (1) 时间拿到最小值(新手理解:不管栈里有多少元素,找最小值都超快)。
**思路:**用两个栈,一个存所有元素(主栈),一个存 "当前最小值"(辅助栈)。
cpp
#include <iostream>
#include <stack>
using namespace std;
class MinStack {
public:
// 压栈:新元素≤辅助栈顶,才放进辅助栈
void push(int x) {
_data.push(x);
// 新手注意:辅助栈空 或 新元素更小,才入栈
if (_min.empty() || x <= _min.top()) {
_min.push(x);
}
}
// 出栈:主栈顶=辅助栈顶,辅助栈才出栈
void pop() {
if (!_data.empty()) {
if (_data.top() == _min.top()) {
_min.pop();
}
_data.pop();
}
}
// 取栈顶
int top() {
return _data.top();
}
// 取最小值(直接拿辅助栈顶)
int getMin() {
return _min.top();
}
private:
stack<int> _data; // 存所有元素
stack<int> _min; // 存最小值
};
// 测试(新手可以直接运行)
int main() {
MinStack ms;
ms.push(3);
ms.push(2);
ms.push(5);
cout << "最小值:" << ms.getMin() << endl; // 输出2
ms.pop();
ms.pop();
cout << "最小值:" << ms.getMin() << endl; // 输出3
return 0;
}
(2)逆波兰表达式求值
新手理解:逆波兰表达式就是把运算符放数字后面,比如 3 4 + 就是 3+4,不用括号,超适合用栈算。
cpp
#include <iostream>
#include <stack>
#include <vector>
#include <string>
using namespace std;
int evalRPN(vector<string>& tokens) {
stack<int> s;
for (string& str : tokens) {
// 是数字:转成int入栈
if (str != "+" && str != "-" && str != "*" && str != "/") {
s.push(stoi(str)); // stoi:字符串转int(新手记住这个函数)
} else {
// 是运算符:出栈两个数计算
int right = s.top(); s.pop(); // 右操作数(后出栈的是右边)
int left = s.top(); s.pop(); // 左操作数
if (str == "+") s.push(left + right);
if (str == "-") s.push(left - right);
if (str == "*") s.push(left * right);
if (str == "/") s.push(left / right); // 题目保证除数≠0
}
}
return s.top(); // 最后栈里只剩结果
}
// 测试(新手可运行)
int main() {
// 计算 (1+2)*3 → 逆波兰表达式:1 2 + 3 *
vector<string> tokens = {"1","2","+","3","*"};
cout << "结果:" << evalRPN(tokens) << endl; // 输出9
return 0;
}
1.5 模拟实现(不用纠结底层)
新手不用写标准库的复杂版本,先实现一个基于 vector 的简单栈,理解核心逻辑:
cpp
#include <iostream>
#include <vector>
using namespace std;
// 新手版:自己写栈
template<class T>
class MyStack {
public:
// 压栈:尾插
void push(const T& x) {
_vec.push_back(x);
}
// 出栈:尾删
void pop() {
if (!empty()) {
_vec.pop_back();
}
}
// 取栈顶
T& top() {
return _vec.back();
}
// 判空
bool empty() {
return _vec.empty();
}
// 大小
int size() {
return _vec.size();
}
private:
vector<T> _vec; // 用vector存元素(新手最熟悉的容器)
};
// 测试
int main() {
MyStack<int> s;
s.push(10);
s.push(20);
cout << s.top() << endl; // 20
s.pop();
cout << s.size() << endl; // 1
return 0;
}
2 🚶 队列(queue):先进先出的 "排队买奶茶"
2.1 新手理解:啥是队列?
排队买奶茶,先到的人先买走,后到的人排最后 ------ 这就是先进先出(FIFO),队列的核心逻辑。
C++ 的 queue 也是容器适配器,默认用 deque 当底层,新手重点记 "队尾进、队头出"。
2.2 必背接口(记 7 个)

💡 避坑:
- front()和back()也要先判断!q.empty(),否则崩溃;
- pop()只删队头,不返回值!
2.3 新手能看懂的代码示例
cpp
#include <iostream>
#include <queue> // 必须包含头文件
using namespace std;
int main() {
queue<int> q;
// 入队(排队)
q.push(1);
q.push(2);
q.push(3);
cout << "队头:" << q.front() << endl; // 1
cout << "队尾:" << q.back() << endl; // 3
cout << "队列大小:" << q.size() << endl; // 3
// 出队(买完走了)
q.pop();
cout << "出队后队头:" << q.front() << endl; // 2
// 遍历队列(新手:边删边看)
while (!q.empty()) {
cout << q.front() << " "; // 2 3
q.pop();
}
return 0;
}
2.4 刷题:用两个队列实现栈
**思路:**两个队列 "倒腾",每次只留最后一个元素当栈顶。
cpp
#include <iostream>
#include <queue>
using namespace std;
class MyStack {
public:
// 压栈:直接放进q1
void push(int x) {
q1.push(x);
}
// 出栈:把q1的元素倒到q2,只剩最后一个
int pop() {
// 把q1除了最后一个,都倒到q2
while (q1.size() > 1) {
q2.push(q1.front());
q1.pop();
}
// 取q1最后一个(栈顶)
int res = q1.front();
q1.pop();
// 交换q1和q2,下次继续用q1
swap(q1, q2);
return res;
}
// 取栈顶:直接取q1队尾
int top() {
return q1.back();
}
// 判空
bool empty() {
return q1.empty() && q2.empty();
}
private:
queue<int> q1;
queue<int> q2;
};
// 测试
int main() {
MyStack ms;
ms.push(1);
ms.push(2);
cout << "栈顶:" << ms.top() << endl; // 2
cout << "出栈:" << ms.pop() << endl; // 2
cout << "栈顶:" << ms.top() << endl; // 1
return 0;
}
2.5 模拟实现
用 list 实现(list 头删效率高,适合队列):
cpp
#include <iostream>
#include <list>
using namespace std;
template<class T>
class MyQueue {
public:
// 入队:尾插
void push(const T& x) {
_list.push_back(x);
}
// 出队:头删
void pop() {
if (!empty()) {
_list.pop_front();
}
}
// 队头
T& front() {
return _list.front();
}
// 队尾
T& back() {
return _list.back();
}
bool empty() {
return _list.empty();
}
int size() {
return _list.size();
}
private:
list<T> _list; // list头删不用搬移元素,比vector快
};
3 🏆 优先队列(priority_queue):自带 "VIP" 的队列
3.1 新手理解:啥是优先队列?
排队买奶茶,VIP 用户不用排最后,直接插前面 ------ 优先队列就是 "谁优先级高,谁先出队",底层是 "堆 "(新手可以理解成 "自动排序的完全二叉树 ")。
默认是大堆(最大值优先),也能改成小堆(最小值优先)。
3.2 接口(记 5 个)

💡 避坑:
- 优先队列没有size()?不,有!只是新手用得少;
- top()是取堆顶,不是队头!优先队列没有front()/back()。
3.3 必学:大堆 vs 小堆
cpp
#include <iostream>
#include <queue>
#include <vector>
#include <functional> // 用greater必须包含这个头文件!
using namespace std;
int main() {
vector<int> v = {3,1,4,2,5};
// 1. 大堆(默认):最大值优先
priority_queue<int> pq1(v.begin(), v.end());
cout << "大堆顶:" << pq1.top() << endl; // 5
// 2. 小堆:最小值优先(新手记格式!)
// 格式:priority_queue<类型, 底层容器, greater<类型>>
priority_queue<int, vector<int>, greater<int>> pq2(v.begin(), v.end());
cout << "小堆顶:" << pq2.top() << endl; // 1
return 0;
}
3.4 新手刷题:找数组中第 K 大的元素
思路:用小堆更高效(新手先学大堆版,简单):
cpp
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
// 找第K大的元素(新手版:大堆)
int findKthLargest(vector<int>& nums, int k) {
// 1. 把所有元素放进大堆
priority_queue<int> pq(nums.begin(), nums.end());
// 2. 删k-1个最大值,剩下的堆顶就是第K大
for (int i = 0; i < k-1; i++) {
pq.pop();
}
return pq.top();
}
// 测试
int main() {
vector<int> nums = {3,2,1,5,6,4};
int k = 2;
cout << "第" << k << "大的元素:" << findKthLargest(nums, k) << endl; // 5
return 0;
}
3.5 新手进阶:存储自定义类型(比如日期)
新手先理解:自定义类型要告诉编译器 "怎么比大小",也就是重载<或>运算符。
cpp
#include <iostream>
#include <queue>
using namespace std;
// 日期类(新手简化版)
class Date {
public:
Date(int y, int m, int d) : _year(y), _month(m), _day(d) {}
// 重载<:用于大堆(比较日期大小)
bool operator<(const Date& d) const {
// 年大的大 → 月大的大 → 天大的大
if (_year != d._year) return _year < d._year;
if (_month != d._month) return _month < d._month;
return _day < d._day;
}
// 重载<<:方便输出
friend ostream& operator<<(ostream& out, const Date& d) {
out << d._year << "-" << d._month << "-" << d._day;
return out;
}
private:
int _year, _month, _day;
};
int main() {
priority_queue<Date> pq;
pq.push(Date(2024, 3, 10));
pq.push(Date(2024, 3, 9));
pq.push(Date(2024, 3, 11));
cout << "优先级最高的日期:" << pq.top() << endl; // 2024-3-11
return 0;
}
4 🧩 容器适配器:"套壳" 的底层逻辑
4.1 新手理解:啥是适配器?
适配器就是 "转换器"------ 比如你有个欧标插头,在中国用不了,买个适配器就能插国标插座。
STL 里的 stack/queue/priority_queue 就是 "容器适配器":它们不自己存数据,而是 "套" 在其他容器(比如 deque/vector/list)外面,只暴露自己需要的接口。
4.2 为啥默认用 deque?(新手大白话)
先简单记 deque 的特点:
- 👍 优点:头尾加 / 删元素快(比 vector 头删快,比 list 省空间);
- 👎 缺点:遍历慢(但 stack/queue 不用遍历,完美避开)。
新手不用纠结 deque 的底层结构,记住:
- stack 只需要 "尾插 / 尾删",deque 比 vector 扩容快;
- queue 需要 "尾插 / 头删",deque 比 list 省空间;
所以 STL 选 deque 当默认底层容器。
4.3 新手能改的底层容器
cpp
#include <iostream>
#include <stack>
#include <vector> // 用vector当stack的底层
#include <queue>
#include <list> // 用list当queue的底层
using namespace std;
int main() {
// 1. stack用vector当底层
stack<int, vector<int>> s;
s.push(1);
s.push(2);
cout << s.top() << endl; // 2
// 2. queue用list当底层
queue<int, list<int>> q;
q.push(1);
q.push(2);
cout << q.front() << endl; // 1
return 0;
}
5 📝 避坑 & 复习总结
5.1 常见错误(避坑指南)
1.调用top()/front()/back()前没判空 → 程序崩溃;
2.以为pop()会返回元素 → 其实pop()只删除,要先top()再pop();
3.用小堆时忘加#include → 编译报错;
4.优先队列用front()/back() → 优先队列只有top();
5.自定义类型放进优先队列,忘重载</> → 编译报错。
5.2 核心考点(复习重点)
1.stack:后进先出,核心接口push/pop/top,经典题:最小栈、逆波兰表达式;
2.queue :先进先出,核心接口push/pop/front/back,经典题:用队列模拟栈;
3priority_queue :堆结构,默认大堆,小堆要加greater,经典题:TopK 问题;
4.容器适配器:stack/queue 不是原生容器,默认底层是 deque,可自定义底层容器。