LeetCode 225 这道题,光看标题就有点绕:“用队列实现栈”。熟悉数据结构的朋友第一反应都是:队列先进先出,栈后进先出,这两个东西的方向天生是反的,怎么能用队列把栈的行为模拟出来?可它偏偏是 LeetCode 栈与队列专题里非常经典的一道题,也是面试里出现频率不低的一道“思维题”。
题目本身不复杂,但它很巧妙地刺中了一个点:你到底是真的理解栈和队列的进出顺序,还是只是会背“栈顶、队尾”几个名词。如果你正在刷 LeetCode 准备面试,或者刚学完数据结构想找点题巩固一下对队列的理解,这道题都值得认真做一遍。它能让你看清楚一件事:在不改变底层容器的情况下,通过调整操作顺序,就能让一个 FIFO(先进先出)结构表现出 LIFO(后进先出)的语义。
我最早刷这题时也走了弯路,总想着“给队列加点什么能力”,或者直接拿双端队列取队尾作弊。后来想通了才明白:这题考的不是“怎么修改队列”,而是“怎么利用队列的基本操作,把最后进来的元素推到最前面”。这篇文章我就把三种主流解法、复杂度对比、容易踩的坑,以及栈和队列在真实工程中的对应场景一次说清楚。
1. 题目到底在问什么:接口约束才是核心
1.1 把题意翻成人话
先翻译一下题目。LeetCode 225 要求你设计一个类 MyStack,它对外提供四个方法:
push(x):将元素 x 压入栈顶pop():移除并返回栈顶元素top():返回栈顶元素,但不移除empty():返回栈是否为空
关键限制是:你只能使用“队列的标准操作”。什么意思?在 Java 里,如果你把队列声明为 Queue<Integer>,你只能用这些方法:
offer(e):在队尾加入元素poll():移除并返回队首元素peek():返回队首元素但不移除size():返回队列元素个数isEmpty():判断队列是否为空
你不能用 getLast(),不能用 pollLast(),更不能直接声明一个 Deque 然后说“这不也是队列吗”。题目要求的就是在最朴素的 FIFO 接口下,模拟出 LIFO 的语义。
这本质上是一个“适配器问题”。栈和队列对外暴露的接口很像,都是“往里放一个元素、往外取一个元素”,区别只在于取的时候到底轮到谁。栈取的是最后一个进来的,队列取的是最早进来的。要做的事情只有一个:把元素的出场顺序反转一下。
1.2 前置知识:栈和队列的本质对比
栈和队列可能是数据结构里最容易被轻视的两个概念,因为代码太简单了。但它们在计算机系统里的地位非常重,几乎所有程序的运行都依赖栈,几乎所有系统间的通信都依赖队列。
先看基础定义:
- 栈:后进先出(LIFO),只能在“栈顶”插入和删除。就像一叠盘子,后放上去的盘子总是最先被拿走。
- 队列:先进先出(FIFO),只能从“队尾”入队,从“队首”出队。就像食堂排队,先来的先打饭。
这道题“用队列实现栈”,就是在明确告诉你:你的存储容器底层只有 FIFO 这一种能力,但用户要求它表现出 LIFO 的行为。这就像服务员只有一排椅子,但顾客非要“后到先坐”。
用生活类比来感受一下:你在奶茶店排队,队首的人先点单。这时候来了一个 VIP,想插队到最前面,但规则不允许直接从队首插入。怎么办?最简单的办法是让前面所有人先挪到另一个等待区,让 VIP 站到队首,再把其他人按顺序排回 VIP 后面。这个“挪人”的过程,恰恰就是双队列实现栈的核心思想。
1.3 和“用栈实现队列”做对照
LeetCode 还有一道镜像题:232. 用栈实现队列。这两道题经常被放在一起对比。
用栈实现队列,思路很直观:准备两个栈,一个负责入队,一个负责出队。入队时直接往 inStack 里压,出队时如果 outStack 是空的,就把 inStack 里的元素全部倒进 outStack。因为栈是 LIFO,倒一次之后,底部的元素变成了顶部,顺序就反转过来了。这里用了一个很自然的“负负得正”思路:第一个栈逆序一次,再逆序一次就变回正序。
而用队列实现栈,反而没那么直观。因为队列本身就是正序的,你没法通过“把元素倒到另一个队列”来反转顺序——两个队列各倒一次,顺序还是正的。所以这道题真正的解法思路,不是“反转顺序”,而是“让新元素想办法排到队首”。理解这个区别,是解开这道题的第一步。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 方法一:push 时倒腾,让栈顶永远在队首
2.1 思路推导:谁最晚进,谁就应该在队首
既然栈顶元素必须是“最后进来的”那个,而队列里能最先被取出的位置是队首,那么最自然的想法就出来了:如果我能保证新入栈的元素永远排在队首,那 pop() 和 top() 就都是 O(1) 的操作,直接操作队首即可。
问题是,队列只允许从队尾入队。新元素来了之后天然排在最后面,怎么才能让它跑到最前面?
答案是:借助一个辅助队列,把“旧元素”整体挪到“新元素”后面。
具体过程拆开看:
- 新元素 x 来了,先放在辅助队列
helper里。此时helper中只有 x。 - 把主队列
queue里的所有元素,按顺序依次挪到helper的队尾。 - 交
