LeetCode-225-使用两个队列实现-LIFO-栈
目录
LeetCode - #225 使用两个队列实现 LIFO 栈
网罗开发
(小红书、快手、视频号同名)
大家好,我是 展菲 ,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括 iOS、前端、Harmony OS、Java、Python 等方向。在 移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源 等领域有深厚造诣。
****图书作者:《ESP32-C3 物联网工程开发实战》
****图书作者:《SwiftUI 入门,进阶与实战》
****超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾************
摘要
在 Swift 语言中,原生并没有提供基于队列的栈实现。本篇文章将介绍如何 使用两个队列模拟 LIFO 栈 ,并提供完整的 Swift 代码实现。我们会分析代码逻辑、测试示例,并探讨时间和空间复杂度,帮助开发者理解该方法的核心原理。
描述
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(
push
、
top
、
pop
和
empty
)。
实现
MyStack
类:
void push(int x)
将元素 x 压入栈顶。int pop()
移除并返回栈顶元素。int top()
返回栈顶元素。boolean empty()
如果栈是空的,返回true
;否则,返回false
。
注意:
- 你只能使用队列的标准操作 —— 也就是
push to back
、peek/pop from front
、size
和is empty
这些操作。 - 你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
示例:
输入:
["MyStack", "push", "push", "top", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]
解释:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False
提示:
1 <= x <= 9
- 最多调用
100
次push
、pop
、top
和empty
- 每次调用
pop
和top
都保证栈不为空
进阶: 你能否仅用一个队列来实现栈。
题解答案
我们可以使用
两个队列
queue1
和
queue2
来模拟栈的行为。实现方式如下:
push(x)
:- 直接将
x
添加到queue1
。
- 直接将
pop()
:- 将
queue1
中的 前 n-1 个元素 依次移动到queue2
。 queue1
中仅剩下最后一个元素,即栈顶元素,弹出它并返回。- 交换
queue1
和queue2
,使queue1
始终作为主要存储队列。
- 将
top()
:- 先执行与
pop()
相同的步骤,但不移除栈顶元素,而是返回它。 - 依然交换
queue1
和queue2
,保持队列状态不变。
- 先执行与
empty()
:- 直接判断
queue1
是否为空。
- 直接判断
题解代码
Swift 实现
import Foundation
class MyStack {
private var queue1: [Int] = []
private var queue2: [Int] = []
init() {}
func push(_ x: Int) {
queue1.append(x)
}
func pop() -> Int {
while queue1.count > 1 {
queue2.append(queue1.removeFirst())
}
let topElement = queue1.removeFirst()
swap(&queue1, &queue2)
return topElement
}
func top() -> Int {
while queue1.count > 1 {
queue2.append(queue1.removeFirst())
}
let topElement = queue1.first!
queue2.append(queue1.removeFirst())
swap(&queue1, &queue2)
return topElement
}
func empty() -> Bool {
return queue1.isEmpty
}
}
// 示例测试
let myStack = MyStack()
myStack.push(1)
myStack.push(2)
print(myStack.top()) // 输出 2
print(myStack.pop()) // 输出 2
print(myStack.empty()) // 输出 false
题解代码分析
1. push(x)
- 时间复杂度 :O(1) —— 直接插入队列末尾,时间复杂度为 O(1)。
- 空间复杂度
:O(1) —— 仅存储额外的
x
,不会增加额外数据结构的空间。
2. pop()
- 核心逻辑
:
- 将
queue1
中的n-1
个元素逐个转移到queue2
。 queue1
中最后一个元素即为栈顶元素,将其移除并返回。- 交换
queue1
和queue2
角色,确保queue1
始终是主要存储队列。
- 将
- 时间复杂度
:O(n) —— 需要移动
n-1
个元素。 - 空间复杂度
:O(1) —— 只使用
queue1
和queue2
两个队列。
3. top()
- 核心逻辑
:
- 先执行
pop()
的前半部分,将n-1
个元素转移到queue2
。 - 记录
queue1
剩余的最后一个元素(即栈顶)。 - 将该元素移入
queue2
,再交换queue1
和queue2
。
- 先执行
- 时间复杂度
:O(n) —— 需要移动
n-1
个元素。 - 空间复杂度
:O(1) —— 只使用
queue1
和queue2
。
4. empty()
- 时间复杂度
:O(1) —— 直接判断
queue1
是否为空。 - 空间复杂度 :O(1) —— 仅返回布尔值。
示例测试及结果
let myStack = MyStack()
myStack.push(1)
myStack.push(2)
print(myStack.top()) // 输出 2
print(myStack.pop()) // 输出 2
print(myStack.empty()) // 输出 false
输出:
2
2
false
时间复杂度分析
操作 | 时间复杂度 |
---|---|
push(x) | O(1) |
pop() | O(n) |
top() | O(n) |
empty() | O(1) |
由于
pop()
和
top()
需要移动
n-1
个元素,时间复杂度较高。
空间复杂度分析
操作 | 空间复杂度 |
---|---|
push(x) | O(1) |
pop() | O(1) |
top() | O(1) |
empty() | O(1) |
由于我们 只使用两个队列 ,因此空间复杂度始终为 O(1) 。
总结
- 本题使用两个队列成功模拟了 LIFO 栈
,并实现了
push
、pop
、top
和empty
操作。 push
操作较快(O(1)),但pop
和top
需要 O(n) 时间 ,因为它们涉及数据的转移。- 优化方向
:
- 若希望提升
pop()
的效率,可以改用 单队列 方法(进阶解法),这样pop()
可在 O(1) 时间内完成。 - 但是
单队列方法会使
push(x)
变为 O(n) ,两者是互斥的。
- 若希望提升