第3章|栈与队列
Unit 05 · 两次顺序反转恢复 FIFO
两个后进先出的栈,
为什么能恢复先进先出?
第一次反转发生在元素进入输入栈时;第二次反转发生在从输入栈整体搬入输出栈时。真正困难的不是“倒一次”,而是确定什么时候不该再倒。
题目要求
仅使用两个栈实现一个先进先出队列。实现 MyQueue,支持在队尾加入元素、移除并返回队首、读取队首以及判断队列是否为空。
约束与进阶
1 <= x <= 9;- 最多调用 100 次四类接口;
- 所有
pop / peek调用均有效; - 进阶要求每个操作均摊时间复杂度为
O(1)。
示例
本页主模型
input_stack 接收新元素;output_stack 提供队首。只有当输出栈为空且需要读取队首时,才将输入栈整体搬入输出栈。
保存尚未转移的较新元素。新元素只需在数组尾部 append,保持 O(1)。
保存已经反转的较旧元素。其栈顶恰好是当前队首。
只有输出栈为空时才转移。未空时保留旧元素的服务优先级。
单次转移可能是 O(n),但每个元素一生只会从输入栈搬到输出栈一次。
从 Unit 04 到 Unit 05:从“同步摘要”走向“分区状态”
上一单元用主栈与辅助栈同步维护同一容器的极值;本单元则把同一个逻辑队列拆成“较新批次”和“较旧批次”。两者共同要求:先定义每个容器代表什么,再证明每次修改后整体语义仍成立。
data_stack 保存真实元素,min_stack 保存查询摘要。两个结构需要同步修改。input_stack 保存较新元素,output_stack 保存已反转的较旧元素。两个结构共同组成一个逻辑队列。input_stack 新元素入口
output_stack 栈顶 = 队首
当前状态下,哪个元素应该最先离开?
迁移题:LeetCode #225 用队列实现栈
先读完整原题,再判断 #232 的哪些思想能够保留。目标从 FIFO 变为 LIFO,底层限制也从栈变为队列;单队列方案通过在每次 push 后轮转旧元素,让队首始终代表栈顶。
LeetCode #225|用队列实现栈
Easy · Stack · Design · Queue题目要求
只使用队列实现一个后进先出(LIFO)栈,并支持普通栈的四种操作:压入栈顶、移除并返回栈顶、读取栈顶,以及判断是否为空。
允许的底层操作
- 只能使用队列的标准操作:从队尾加入、从队首查看或删除、读取长度、判断为空。
- Python 可以使用
deque模拟,但不能借助右端弹出绕过 FIFO 限制。
官方示例
约束与进阶
1 ≤ x ≤ 9- 最多调用 100 次四类接口。
pop与top调用时保证栈非空。- 进阶:仅使用一个队列实现。
读题检查 1:top() 与 pop() 应访问哪一个元素?
读题检查 2:官方示例连续压入 1、2 后,为什么 top() 返回 2?
#232|用栈实现队列
- 底层:LIFO 栈;目标:FIFO 队列。
- 新元素先进入
input_stack。 - 第一次需要队首且
output_stack为空时,整体转移。 - 重排成本放在部分
pop/peek上,均摊 O(1)。
#225|用队列实现栈(单队列)
- 底层:FIFO 队列;目标:LIFO 栈。
- 新元素先从队尾入队。
- 立即把此前元素从队首依次移到队尾。
- 重排成本放在
push上,使pop/top可直接访问队首。
哪一句最准确描述这次迁移?
槽位 1|状态表示
用什么保存当前逻辑栈?
槽位 2|接收新元素
push(x) 的第一步是什么?
槽位 3|恢复 LIFO 顺序
新元素入队后如何让它成为队首?
槽位 4|公开接口
轮转完成后,pop/top/empty 如何实现?
由四个语义槽位拼装出的单队列版本
运行官方样例
可选挑战:不看完整答案独立重写
只使用 append、popleft、队首读取、长度和判空。检查器会验证四个接口及 push 后轮转。
Unit 05 Skill 卡
看到“只能使用栈却要实现先进先出”时,不要每次查询都恢复完整队列。将新旧元素分区:新元素进入 input,旧元素在 output 中按服务顺序离开。