这一章的三位主角是数据结构界的「规矩之王」:只允许在特定位置进出。听起来很受气?恰恰是这种限制成就了它们的价值 🎯。
🥞 栈(Stack):后进先出
想象一摞盘子:只能从顶端放(push)、从顶端取(pop)、偷看顶端(top)。最后放上去的盘子最先被拿走——LIFO(Last-In-First-Out)。
生活原型:浏览器后退按钮 ⬅️。每访问一个网页 push 一次,按后退 pop 一次——最近看过的页面最先回来。还有编辑器的撤销(Ctrl+Z)、函数调用栈……全是栈的化身。
书里用数组实现了一个泛型栈:top 记录栈顶下标,push 就 ++top 再放元素,pop 就取出后 --top——所有操作 O(1)。
🧪 实战一:括号匹配
编译器怎么检查 ((a+b)*c) 没写错?扫描 + 栈:
- 遇到左括号
(、[、{→ push; - 遇到右括号 → pop,检查两边是否配对;
- 结束时栈必须为空,否则有未闭合括号。
一行代码都不难,但思路极优雅——问题的嵌套结构,天然匹配栈的 LIFO。书里把它扩展到 HTML 标签匹配(<b>...</i> 这种错误一查一个准)。
🧋 队列(Queue):先来先服务
排队买奶茶 ☕:队尾入队(enqueue),队头出队(dequeue)——FIFO(First-In-First-Out),世界上最公平的规则。
⚠️ 新手陷阱:用数组实现队列,如果只用「头指针 + 尾指针一直右移」,数组左侧空间全浪费。正确姿势是循环数组(环形缓冲区):下标到末尾就绕回开头,index = (index + 1) % N。
🍔 双端队列(Deque):两头通吃
double-ended queue:头尾都能进、都能出。可以看成栈和队列的合体——只用一端就是栈,一头进一头出就是队列。C++ STL 的 std::deque 就是它,也是 std::stack 和 std::queue 的默认底层实现。
🎯 本章通关清单
- [ ] 白板实现一个数组栈,说明每个操作为何 O(1)
- [ ] 用栈写括号匹配,说清三种失败情形(不配对 / 栈空 pop / 结束时非空)
- [ ] 解释循环数组如何解决队列的空间浪费
- [ ] 举出栈和队列各两个真实应用
栈和队列都是「线性」世界,下一章把线性 ADT 抽象得更彻底——向量、列表与迭代器 ⚔️