C++ 进阶 — 第 4 课
像一摞盘子:最后放上去的,最先拿下来。
#include <stack>
stack<int> s;
s.push(1); // 放入 1
s.push(2); // 放入 2
s.push(3); // 放入 3
cout << s.top(); // 3(只看栈顶,不删)
s.pop(); // 弹出栈顶(删掉 3)
cout << s.size(); // 2(还剩 1, 2)
| 操作 | 代码 | 说明 |
|---|---|---|
| 入栈 | s.push(x) | 放到栈顶 |
| 出栈 | s.pop() | 删栈顶,不返回值 |
| 看栈顶 | s.top() | 返回栈顶元素 |
| 大小 | s.size() | 现在还剩几个元素 |
| 是否空 | s.empty() | 空的时候返回 true |
bool check(string s) {
stack<char> st;
for (char c : s) {
if (c == '(') st.push(c);
else if (c == ')') {
if (st.empty()) return false; // 右括号多了
st.pop();
}
}
return st.empty(); // 栈空才算匹配
}
为什么用栈?因为一个右括号只能和「最近一个还没配对的左括号」配对 —— 最近的先出,正好是后进先出。最后那行 return st.empty(); 是在查「左括号多了」的情况,两个方向都要查。
像排队:先来的先处理。
#include <queue>
queue<int> q;
q.push(1); // 入队
q.push(2);
q.push(3);
cout << q.front(); // 1(队首,不删)
cout << q.back(); // 3(队尾,不删)
q.pop(); // 出队(删掉 1)
cout << q.front(); // 2
| 操作 | 代码 | 说明 |
|---|---|---|
| 入队 | q.push(x) | 放到队尾 |
| 出队 | q.pop() | 删队首,不返回值 |
| 队首 | q.front() | 返回队首元素 |
| 队尾 | q.back() | 返回队尾元素 |
注意和栈一样:pop() 只负责删,不负责把删掉的东西交给你。想要那个值,得先用 front() / top() 存一份。
不是按「先来后到」,而是按值的大小出队。
#include <queue> // priority_queue 也在 queue 头文件里
priority_queue<int> pq;
pq.push(3);
pq.push(5);
pq.push(1);
cout << pq.top(); // 5(最大)
pq.pop();
cout << pq.top(); // 3
默认大顶堆(大的先出)。想要小的先出:
priority_queue<int, vector<int>, greater<int>> pq; // 小顶堆
三个模板参数要写全:元素类型、底层容器、比较器。少写一个都编译不过 —— 这也是初学者最常卡住的一行。
| 操作 | 代码 | 复杂度 |
|---|---|---|
| 入队 | pq.push(x) | O(log n) |
| 出队 | pq.pop() | O(log n) |
| 看最大 | pq.top() | O(1) |
为什么 top() 是 O(1) 而 push / pop 是 O(log n)?因为堆只保证「堆顶最大」,一看就有答案;而进来一个、出去一个,都要沿着树往上或往下挪几层,层数是 log n。
| stack | queue | priority_queue | |
|---|---|---|---|
| 出队规则 | 后进先出 | 先进先出 | 按大小 |
| 日常类比 | 摞盘子 | 排队 | 急诊(重的先看) |
| 经典题 | 括号匹配 | BFS 广搜 | Top K、Dijkstra |
| 只暴露 | 栈顶一头 | 队首、队尾两头 | 堆顶一头 |
stack 底层就是 vector,只是把 push_back 改名 push,把 pop_back 改名 pop,然后禁掉中间插入:
vector<int> v;
v.push_back(1); v.push_back(2); // 代替 push
cout << v.back(); // 代替 top()
v.pop_back(); // 代替 pop()
pop() 不返回元素:它只删不给。想要那个值,先 top() / front() 存一份,再 pop()。top() / front() / pop() 是未定义行为,程序可能直接崩。priority_queue<int, vector<int>, greater<int>>,只写 greater 不行。top() + pop(),而且这样会把堆清空。return st.empty();,否则 (() 这种会判成匹配。stack: push(1) push(2) push(3) pop() pop() top() 结果?() 的表达式,判断括号是否匹配。( 入栈,遇 ) 出栈。
priority_queue<int, vector<int>, greater<int>> pq;
选做:把第 5 节的 vector 写法包成一个 struct MyStack,只公开 push / pop / top / size / empty,内部用 vector 存。写完你就明白「限制操作」是什么意思了。
()、)(、(() 三组样例,看三个答案是不是都对的。pop() / top() / front() 之前问一句:这时候容器会不会是空的?return st.empty();?漏了它,左括号多的串会被判成匹配。push/pop 是 O(log n),top 是 O(1)。