用两个栈实现一个队列。
未标记
考查重点:栈间转移与摊还复杂度。入队压输入栈,出队栈空才把输入逐项倒过去,再弹顶;每元素最多搬一次,摊还O(1)
本题目录
参考答案
本答案基于相关资料,由 AI 辅助编写,供学习与面试准备参考。如有疏漏,欢迎反馈指正。
核心回答
用一个输入栈接收入队元素,一个输出栈负责出队。输入栈中的后进先出顺序,经过一次整体倒入输出栈后,再反转成队列需要的先进先出顺序。只有输出栈为空时才搬运,输出栈还有旧元素时不能把新元素盖到它上面。
每个元素最多进入输入栈一次、转移一次、出队一次,因此一串操作的总成本与操作数成正比,入队与出队的摊还时间为 O(1)。单次触发整体搬运的出队仍可能是 O(n),空间复杂度为 O(n)。
Python 实现
空队列的 pop() 和 peek() 抛出 IndexError;peek() 查看而不删除。这里使用 list 尾部作为栈顶,不能使用头部删除来假装栈操作。
class TwoStackQueue:
def __init__(self):
self.inbox, self.outbox = [], []
def push(self, value):
self.inbox.append(value)
def _prepare(self):
if not self.outbox:
while self.inbox:
self.outbox.append(self.inbox.pop())
def pop(self):
self._prepare()
if not self.outbox:
raise IndexError("empty queue")
return self.outbox.pop()
def peek(self):
self._prepare()
if not self.outbox:
raise IndexError("empty queue")
return self.outbox[-1]
def empty(self):
return not self.inbox and not self.outbox
为什么只在输出栈为空时搬运
依次入队 1,2,输出栈搬成 [2,1],第一次出队得到 1。此时再入队 3,队头仍是输出栈里的 2;等 2 出队后,才把输入栈里的 3 搬过来。
如果每次出队都把输入栈倒入已有输出栈,新元素会盖住尚未出队的旧元素,破坏先后顺序。队列为空则要求两个栈同时为空,不能只看其中一个。
该版本只定义单线程容器行为。多线程中需要把检查、搬运和取值作为同一临界区保护,两个栈各自线程安全并不能自动保证整个队列正确。
参考资料
- 国内:代码随想录:用栈实现队列:采用输出栈为空才整体搬运的策略,复杂度明确区分单次O(n)与摊还O(1)。
- 国内:JavaGuide:线性数据结构:采用栈与队列的先后顺序约束,说明单个栈操作不等于整个队列操作。
- 国内:JavaGuide:集合常见问题(上):参考队列与双端队列的入出顺序和空队列接口,确定本题只在栈顶操作、空队列立即抛错的契约。
- 国内:JavaGuide:ArrayList源码分析:借鉴动态数组尾部操作与扩容的成本分析,区分单次最坏耗时和摊还成本;
Python接口另按官方资料核对。 - 国外:Python:Data Structures:核对
list作为栈的append()/pop()用法,以及普通队列不适合反复删除列表头部。 - 国外:Python:Sequence Types:核对列表尾部删除、索引与空列表访问行为,落实示例接口。
相关面经
3 篇收录题集
编程与数据结构算法
实现队列: 「字节跳动」
