概述

stackqueuepriority_queue 在 C++ STL 中有一个特殊的身份——它们不是独立容器,而是容器适配器(Container Adapter)

这意味着它们没有自己的底层存储,而是基于其他容器(默认 deque)实现的。它们通过限制底层容器的接口,只暴露特定操作,从而实现栈和队列的语义。

1
2
3
4
5
6
7
8
9
10
11
12
  ┌─────────┐
│ stack │ 只允许顶部操作
├─────────┤
│ queue │ 只允许两端操作
├─────────┤
│priority │ 只允许顶部操作(优先级)
│ _queue │
└────┬────┘
│ 适配
┌──────┴──────┐
│ deque/vector │ (底层容器)
└─────────────┘

stack —— 栈

栈是后进先出(LIFO, Last In First Out)的数据结构。就像一摞盘子,最后放上去的最先被拿走。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <stack>

int main() {
std::stack<int> s;

// 入栈
s.push(1);
s.push(2);
s.push(3);

std::cout << "top: " << s.top() << std::endl; // 3
std::cout << "size: " << s.size() << std::endl; // 3
std::cout << "empty: " << s.empty() << std::endl; // false (0)

// 出栈
while (!s.empty()) {
std::cout << s.top() << " "; // 3 2 1
s.pop();
}
std::cout << std::endl;

return 0;
}

stack 的完整接口

操作 说明 复杂度
push(x) 入栈 O(1)
pop() 出栈(无返回值!) O(1)
top() 访问栈顶元素 O(1)
size() 元素数量 O(1)
empty() 是否为空 O(1)
emplace(args...) 原地构造入栈(C++11) O(1)

指定底层容器

1
2
3
4
5
6
7
8
9
10
11
12
#include <stack>
#include <vector>
#include <list>

// 默认用 deque
std::stack<int> s1;

// 用 vector 作为底层
std::stack<int, std::vector<int>> s2;

// 用 list 作为底层
std::stack<int, std::list<int>> s3;

实际应用:括号匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <stack>
#include <string>

bool isValidParentheses(const std::string& s) {
std::stack<char> st;

for (char c : s) {
if (c == '(' || c == '[' || c == '{') {
st.push(c);
} else if (c == ')' || c == ']' || c == '}') {
if (st.empty()) return false;

char top = st.top();
st.pop();

if ((c == ')' && top != '(') ||
(c == ']' && top != '[') ||
(c == '}' && top != '{')) {
return false;
}
}
}
return st.empty();
}

int main() {
std::cout << std::boolalpha;
std::cout << isValidParentheses("()") << std::endl; // true
std::cout << isValidParentheses("()[]{}") << std::endl; // true
std::cout << isValidParentheses("(]") << std::endl; // false
std::cout << isValidParentheses("([)]") << std::endl; // false
std::cout << isValidParentheses("{[]}") << std::endl; // true
std::cout << isValidParentheses("(") << std::endl; // false

return 0;
}

实际应用:表达式求值(后缀表达式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <stack>
#include <string>
#include <cctype>

int evaluatePostfix(const std::string& expr) {
std::stack<int> st;

for (char c : expr) {
if (isspace(c)) continue;

if (isdigit(c)) {
st.push(c - '0');
} else {
int b = st.top(); st.pop();
int a = st.top(); st.pop();

switch (c) {
case '+': st.push(a + b); break;
case '-': st.push(a - b); break;
case '*': st.push(a * b); break;
case '/': st.push(a / b); break;
}
}
}
return st.top();
}

int main() {
// "3 4 + 2 *" = (3+4)*2 = 14
std::cout << evaluatePostfix("3 4 + 2 *") << std::endl; // 14

// "5 1 2 + 4 * + 3 -" = 5+((1+2)*4)-3 = 14
std::cout << evaluatePostfix("5 1 2 + 4 * + 3 -") << std::endl; // 14

return 0;
}

queue —— 队列

队列是先进先出(FIFO, First In First Out)的数据结构。就像排队买票,先到先服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <queue>

int main() {
std::queue<int> q;

// 入队
q.push(1);
q.push(2);
q.push(3);

std::cout << "front: " << q.front() << std::endl; // 1
std::cout << "back: " << q.back() << std::endl; // 3
std::cout << "size: " << q.size() << std::endl; // 3

// 出队
while (!q.empty()) {
std::cout << q.front() << " "; // 1 2 3
q.pop();
}
std::cout << std::endl;

return 0;
}

queue 的完整接口

操作 说明 复杂度
push(x) / emplace(x) 入队 O(1)
pop() 出队(无返回值!) O(1)
front() 访问队首 O(1)
back() 访问队尾 O(1)
size() 元素数量 O(1)
empty() 是否为空 O(1)

指定底层容器

1
2
3
4
5
6
7
8
#include <queue>
#include <list>

// 默认用 deque
std::queue<int> q1;

// 用 list 作为底层
std::queue<int, std::list<int>> q2;

⚠️ queue 不能用 vector 作为底层容器,因为 vector 不支持 pop_front()

实际应用:BFS 层序遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream>
#include <queue>
#include <vector>

struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int v) : val(v), left(nullptr), right(nullptr) {}
};

std::vector<std::vector<int>> levelOrder(TreeNode* root) {
std::vector<std::vector<int>> result;
if (!root) return result;

std::queue<TreeNode*> q;
q.push(root);

while (!q.empty()) {
int levelSize = q.size();
std::vector<int> level;

for (int i = 0; i < levelSize; i++) {
TreeNode* node = q.front();
q.pop();
level.push_back(node->val);

if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
result.push_back(level);
}
return result;
}

int main() {
// 1
// / \
// 2 3
// / \
// 4 5
TreeNode* root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->left = new TreeNode(4);
root->left->right = new TreeNode(5);

auto result = levelOrder(root);
for (auto& level : result) {
for (int x : level) {
std::cout << x << " ";
}
std::cout << std::endl;
}
// 1
// 2 3
// 4 5

return 0;
}

priority_queue —— 优先队列

优先队列是一个实现的容器适配器,默认是最大堆。每次取出的不是最先入队的元素,而是优先级最高(最大或最小)的元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <queue>

int main() {
// 默认最大堆
std::priority_queue<int> pq;

pq.push(3);
pq.push(1);
pq.push(4);
pq.push(1);
pq.push(5);

// 每次取最大值
while (!pq.empty()) {
std::cout << pq.top() << " "; // 5 4 3 1 1
pq.pop();
}
std::cout << std::endl;

return 0;
}

最小堆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <queue>

int main() {
// 最小堆
std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap;

minHeap.push(3);
minHeap.push(1);
minHeap.push(4);
minHeap.push(1);
minHeap.push(5);

while (!minHeap.empty()) {
std::cout << minHeap.top() << " "; // 1 1 3 4 5
minHeap.pop();
}
std::cout << std::endl;

return 0;
}

自定义类型优先队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <queue>
#include <string>

struct Task {
std::string name;
int priority;

bool operator<(const Task& other) const {
// 优先级高的先出队(priority 越大越优先)
return priority < other.priority;
}
};

int main() {
std::priority_queue<Task> pq;

pq.push({"Low priority task", 1});
pq.push({"High priority task", 3});
pq.push({"Medium priority task", 2});

while (!pq.empty()) {
auto task = pq.top();
pq.pop();
std::cout << task.name << " (priority: " << task.priority << ")" << std::endl;
}
// High priority task (priority: 3)
// Medium priority task (priority: 2)
// Low priority task (priority: 1)

return 0;
}

实际应用:Top K 问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <queue>
#include <vector>

// 求 Top K 大元素 —— 维护一个大小为 K 的最小堆
std::vector<int> topK(const std::vector<int>& nums, int k) {
std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap;

for (int x : nums) {
minHeap.push(x);
if ((int)minHeap.size() > k) {
minHeap.pop(); // 弹出当前最小的,保留较大的 k 个
}
}

std::vector<int> result;
while (!minHeap.empty()) {
result.push_back(minHeap.top());
minHeap.pop();
}
return result;
}

int main() {
std::vector<int> nums = {3, 2, 1, 5, 6, 4, 8, 7, 9, 10};
auto result = topK(nums, 3);

std::cout << "Top 3: ";
for (int x : result) {
std::cout << x << " "; // 8 9 10
}
std::cout << std::endl;

return 0;
}

实际应用:合并 K 个有序链表(堆方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>
#include <queue>
#include <vector>

struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(nullptr) {}
};

// 自定义比较器,用于最小堆
struct CompareNode {
bool operator()(ListNode* a, ListNode* b) {
return a->val > b->val; // 最小堆
}
};

ListNode* mergeKLists(std::vector<ListNode*>& lists) {
std::priority_queue<ListNode*, std::vector<ListNode*>, CompareNode> pq;

// 把每个链表的头节点加入堆
for (auto* node : lists) {
if (node) pq.push(node);
}

ListNode dummy(0);
ListNode* tail = &dummy;

while (!pq.empty()) {
ListNode* node = pq.top();
pq.pop();
tail->next = node;
tail = tail->next;

if (node->next) {
pq.push(node->next);
}
}

return dummy.next;
}

三种适配器对比

特性 stack queue priority_queue
数据结构 LIFO 栈 FIFO 队列
默认底层容器 deque deque vector
插入 push() push() push()
删除 pop() pop() pop()
访问 top() front()/back() top()
遍历 ❌ 不支持 ❌ 不支持 ❌ 不支持
排序 按插入顺序(逆序) 按插入顺序 按优先级

常见陷阱

1. pop() 不返回值!

1
2
3
4
5
6
7
8
9
std::stack<int> s;
s.push(42);

// ❌ 这样写会丢失值!
// int val = s.pop(); // 编译错误!pop() 返回 void

// ✅ 正确写法
int val = s.top(); // 先取值
s.pop(); // 再弹出

这是初学者最常犯的错误。STL 设计成 pop() 不返回值是有原因的:如果 pop() 返回值,在返回过程中如果发生异常(拷贝构造失败),元素已经被弹出了,就丢失了。分离 top()pop() 更安全。

2. 不要试图遍历适配器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
std::stack<int> s;
s.push(1);
s.push(2);
s.push(3);

// ❌ stack/queue/priority_queue 没有迭代器
// for (auto x : s) { ... } // 编译错误!

// ✅ 如果需要遍历,只能不断 pop(会销毁数据)
std::stack<int> temp = s; // 拷贝一份
while (!temp.empty()) {
std::cout << temp.top() << " ";
temp.pop();
}

3. priority_queue 不支持修改已有元素

1
2
3
4
5
6
7
8
9
std::priority_queue<int> pq;
pq.push(5);
pq.push(3);
pq.push(8);

// ❌ 没有直接修改堆中元素的方法
// 如果优先级发生变化,只能删除旧的,插入新的

// ✅ 在需要修改优先级的场景,考虑用 set 或手动实现

4. priority_queue 的模板参数顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 完整模板:priority_queue<T, Container, Compare>
// ⚠️ 注意三个参数的顺序!

// 最大堆(默认)
std::priority_queue<int> pq1;
// 等价于
std::priority_queue<int, std::vector<int>, std::less<int>> pq1_full;

// 最小堆
std::priority_queue<int, std::vector<int>, std::greater<int>> pq2;

// 自定义类型
auto cmp = [](const auto& a, const auto& b) { return a.val < b.val; };
std::priority_queue<Task, std::vector<Task>, decltype(cmp)> pq3(cmp);

总结

  • stack:DFS、递归转迭代、括号匹配、表达式求值
  • queue:BFS、层序遍历、任务调度、消息队列
  • priority_queue:Top K、Dijkstra、贪心、任务调度(按优先级)

理解适配器模式有助于你更好地使用这些容器,也明白为什么它们不支持迭代器——限制接口正是适配器的意义所在。