代码随想录算法训练营第十天 | 232. 用栈实现队列、225. 用队列实现栈
今日学习的文章链接和视频链接
参考代码随想录
自己看到题目的第一想法
之前学习过栈和队列的数据结构基础,但是两题都没有思路
自己实现过程中遇到哪些困难
- 232. 用栈实现队列中
pop
的实现没有想到要先把所有数据依次从输入栈出栈然后输出栈进栈;peek
没有复用pop
- 225. 用队列实现栈没有想到只用一个队列实现栈的具体做法
今日收获,记录一下自己的学习时长
- 应打卡7月7日,7月10日补打卡,学习时长3hr
- 复习了栈和队列的基础理论知识,包括用数组和链表实现的具体方式和相关接口
- 学习了Python中deque的相关接口,Java中queue的相关接口
- 后续补充225. 用队列实现栈的Java版本
0232. 用栈实现队列
1. 题目描述
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push
、pop
、peek
、empty
):
实现MyQueue
类:
void push(int x)
将元素x
推到队列的末尾
int pop()
从队列的开头移除并返回元素
int peek()
返回队列开头的元素
boolean empty()
如果队列为空,返回true
;否则,返回false
说明:
你 只能 使用标准的栈操作 — 也就是只有push to top
, peek/pop from top
, size
, 和 is empty
操作是合法的。
你所使用的语言也许不支持栈。你可以使用list
或者deque
(双端队列)来模拟一个栈,只要是标准的栈操作即可。
示例 1:
输入:
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]
解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false
2. 解题思路
题目要求设计队列的五个接口:
- 初始化队列
- 将元素
x
插入队列 - 队头元素出队列
- 返回队列开头的元素
- 判断队列是否为空
注意:
- 本题要求只能用标准的栈操作,包括栈顶插入
push to top
,栈顶出栈和返回栈顶元素peek/pop from top
,栈的大小size
,和判断栈是否为空is empty
- 可以用数组定义栈,也可以用其他的数据结构,例如Python中的
deque
,但要注意满足上面条件 - 用栈来实现队列时,需要两个栈,一个输入栈,一个输出栈
复杂度分析:
- 时间复杂度:
push()
和empty()
为O(1)
pop()
和peek()
为O(n)
(因为需要从输入栈依次出栈和输出栈依次进栈)
- 空间复杂度:
O(n)
具体算法:
__init__
初始化队列: 定义两个栈,输入栈stack_in
,输出栈stack_out
void push(int x)
:入队操作,直接插入输入栈stack_in
int pop()
:出队操作。需要先判断队列是否为空,调用队列自己的接口empty()
;先判断输出栈stack_out
是否为空,如果输出栈不为空,输出栈直接出栈pop()
;如果输出栈为空,输入栈stack_in
不为空,需要依次把输入栈里面的元素从栈顶出栈,然后按顺序插入到输出栈中,此时输出栈的栈顶就是最开始插入队列的元素,即队头元素,最后输出栈出栈int peek()
:返回队列头的元素,可以复用pop()
出队操作,也可以重新定义。先用empty()
判断队列是否为空,再判断输出栈stack_out
是否为空,如果不为空,返回输出栈的栈顶元素,即stack_out[-1]
;如果输出栈为空,返回输入栈stack_in
的栈底元素(第一个插入的元素),即stack_in[0]
。empty()
:判断队列是否为空。如果输出栈和输入栈的长度都为0
,则队列为空,返回True
;否则返回False
。
3. 算法实现
3.1 Python
class MyQueue:
def __init__(self):
# 需要两个栈,一个输入栈,一个输出栈
# 用数组做栈
self.stack_in = []
self.stack_out = []
def push(self, x: int) -> None:
# 插入队列时只用插入输入栈
self.stack_in.append(x)
def pop(self) -> int:
# FIFO
# 先判断是否为空
if self.empty():
return None
# 判断输出栈是否有东西,有就输出
if len(self.stack_out) != 0:
return self.stack_out.pop()
# 如果输出栈里没有东西,输入栈里有东西,就把输入栈里的东西依次出栈,然后插入输出栈
for i in range(len(self.stack_in) - 1, -1, -1):
self.stack_out.append(self.stack_in[i])
self.stack_in.pop(i)
# 输出栈出栈
return self.stack_out.pop()
def peek(self) -> int:
# 判断队列是否为空
if self.empty():
return None
# 输出栈为空,返回输入栈栈底
if len(self.stack_out) == 0:
return self.stack_in[0]
# 输出栈不为空,返回输出栈栈顶
return self.stack_out[-1]
def empty(self) -> bool:
# 判断是否为空
return len(self.stack_out) == 0 and len(self.stack_in) == 0
# Your MyQueue object will be instantiated and called as such:
# obj = MyQueue()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.peek()
# param_4 = obj.empty()
3.2 Java
- Java中
stack
类继承自vector
类,所以继承了vector
类的方法;stack
类的empty()
判断当前栈顶元素是否为空,同时stack
类也继承了isEmpty()
方法判断整个vector
是否为空。
class MyQueue {
// 两个栈:输入栈和输出栈
private Stack<Integer> stackIn;
private Stack<Integer> stackOut;
public MyQueue() {
// 初始化两个栈
stackIn = new Stack<>();
stackOut = new Stack<>();
}
public void push(int x) {
// 输入栈进栈
this.stackIn.push(x);
}
public int pop() {
// 判断输出栈是否为空,输出栈出栈
if (!this.stackOut.isEmpty()) {
return this.stackOut.pop();
}
// 输出栈为空,先输入栈依次出栈,输出栈依次进栈,然后栈顶出栈
while (!this.stackIn.isEmpty()) {
this.stackOut.push(this.stackIn.pop());
}
return this.stackOut.pop();
}
public int peek() {
// 复用pop(),输出栈栈顶元素加回栈顶
int top = this.pop();
this.stackOut.push(top);
return top;
}
public boolean empty() {
// 输出栈和输入栈都为空
return this.stackIn.isEmpty() && this.stackOut.isEmpty();
}
}
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.empty();
*/
0225. 用队列实现栈
1. 题目描述
请你仅使用两个队列实现一个后入先出(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
2. 解题思路
题目要求设计栈的五个接口:
- 初始化栈
- 将元素
x
压入栈顶 - 移除并返回栈顶元素
- 返回栈顶元素
- 判断栈是否为空
注意:
- 本题要求只能用标准的队列操作,包括对位插入元素
push to back
,队头出队和返回队头元素peek/pop from front
,队列的大小size
,和判断队列是否为空is empty
- 可以用定义好的数据结构来实现队列,例如Python中的
deque
,Java中的deque
,和Java中的queue
,但要注意满足上面条件 - 初始思路为用两个队列实现栈,两个队列一个为主队列
queue_in
,另一个队列为副队列queue_out
;主队列queue_in
主要用来存数据做备份,副队列queue_out
做出队操作 - 后期可以优化为用一个队列实现栈
复杂度分析:
- 时间复杂度:
pop()
为O(n)
(因为需要从主队列依次出队和副队列依次入队),其他为O(1)
- 空间复杂度:
O(n)
具体算法:
__init__
初始化栈:定义两个队列,主队列queue_in
和副队列queue_out
void push(int x)
:栈顶入栈操作,直接在主队列queue_in
队尾压入元素x
int top()
:先判断队列不为空;然后在主队列queue_in
中除了队尾元素以外的元素依次从队头出队,副队列queue_out
中从队尾依次入队,然后主副队列交换,即queue_in, queue_out = queue_out, queue_in
。此时的副队列(原本的主队列)只剩下一个元素,该元素即栈顶元素,出队操作popleft()
int top()
:先判断队列不为空;返回主队列的最后一个元素,Python中直接返回queue_in[-1]
boolean empty()
:因为只有主队列deque_in
中存了数据,副队列deque_out
中没有数据,直接判断主队列长度是否为0
优化算法:
- 只用一个队列
queue
实现栈 - 其他接口不变,在出栈操作
pop()
中,队列queue
除了队尾元素之前的全部元素依次从队头出队,然后重新从队尾入队,此时队头元素就是栈顶元素,出栈操作即队列出队操作,即queue.popleft()
3. 算法实现
3.1 Python - 两个队列
- Python中使用
deque()
来定义队列,相关方法有队尾插入元素append()
,队头元素出队popleft()
,队列长度len(deque)
,遍历位置x
的元素用deque[x]
class MyStack:
def __init__(self):
# 两个队列
self.queue_in = deque()
self.queue_out = deque()
def push(self, x: int) -> None:
# 主队列压入队尾
self.queue_in.append(x)
def pop(self) -> int:
if self.empty():
return None
# 主队列除了队尾元素依次出队
for i in range(len(self.queue_in) - 1):
self.queue_out.append(self.queue_in.popleft())
# 主副队列交换
self.queue_in, self.queue_out = self.queue_out, self.queue_in
return self.queue_out.popleft()
def top(self) -> int:
if self.empty():
return None
# 栈顶元素一定在主队列中,主队列队尾元素
return self.queue_in[-1]
def empty(self) -> bool:
# 数据都在主队列中,判断主队列是否为空
return len(self.queue_in) == 0
# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()
3.2 Python - 一个队列
class MyStack:
def __init__(self):
# 只用一个队列
self.queue = deque()
def push(self, x: int) -> None:
# 队列压入队尾
self.queue.append(x)
def pop(self) -> int:
if self.empty():
return None
# 队列中除了队尾元素依次出队,然后从队尾入队
for i in range(len(self.queue) - 1):
self.queue.append(self.queue.popleft())
# 栈顶元素就是队头元素
return self.queue.popleft()
def top(self) -> int:
if self.empty():
return None
# 返回队尾元素
return self.queue[-1]
def empty(self) -> bool:
# 队列长度都为0
return len(self.queue) == 0
# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()