题目表述:
请你仅使用两个队列实现一个后入先出(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都保证栈不为空
进阶:你能否仅用一个队列来实现栈。
力扣链接:
思路分析:
题目要求使用两个队列来实现栈的基本功能,上一题我们做了使用两个栈来实现队列的基本功能,这题是相反的。我们的解题方案可以有两种:1.用两个队列来实现,2.用一个队列来实现。我们重点讲一下第二种方案。
首先push方法实现起来非常简单,直接append就可以。然后就是重点的pop方法,这个方法,栈和队列有很大的区别,栈是先进后出,而队列是先进先出,要想用队列实现先进后出,就必须每次pop之前将左边的n-1个元素移除开然后逐个添加到右边,具体操作可以看下面pop方法中的for循环。
top方法的实现与pop大部分类似,不过就是top还需要保持队列的内容不变。
代码分析:
# 方法一,用一个队列来实现
class MyStack:
def __init__(self):
# deque是python的双向队列,类似list,可以快速在队列的头部和尾部添加或删除元素
self.que = deque()
# 这个方法用来实现元素添加
def push(self, x: int) -> None:
# 从右端添加元素。如果从左端添加应使用appendleft()函数
self.que.append(x)
# 这个方法用来实现元素移除,这是关键,使用队列来实现栈
def pop(self) -> int:
# 判断队列是否为空
if self.empty():
return None
# 移除队列左边的元素,然后添加到队列右边,循环这个操作共n-1次,将最右边那个元素弄到最左边去。
for i in range(len(self.que)-1):
self.que.append(self.que.popleft())
# 移除最左边的元素
return self.que.popleft()
def top(self) -> int:
if self.empty():
return None
# 与pop方法中的转换方式相同
for i in range(len(self.que)-1):
self.que.append(self.que.popleft())
# 获取最左边的元素
temp = self.que.popleft()
# 将刚刚popleft掉的元素添加回去最右边,还原队列
self.que.append(temp)
return temp
def empty(self) -> bool:
return not self.que
# 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()
# 方法2,用两个队列来实现。
from collections import deque
class MyStack:
def __init__(self):
"""
Python普通的Queue或SimpleQueue没有类似于peek的功能
也无法用索引访问,在实现top的时候较为困难。
用list可以,但是在使用pop(0)的时候时间复杂度为O(n)
因此这里使用双向队列,我们保证只执行popleft()和append(),因为deque可以用索引访问,可以实现和peek相似的功能
in - 存所有数据
out - 仅在pop的时候会用到
"""
self.queue_in = deque()
self.queue_out = deque()
def push(self, x: int) -> None:
"""
直接append即可
"""
self.queue_in.append(x)
def pop(self) -> int:
"""
1. 首先确认不空
2. 因为队列的特殊性,FIFO,所以我们只有在pop()的时候才会使用queue_out
3. 先把queue_in中的所有元素(除了最后一个),依次出列放进queue_out
4. 交换in和out,此时out里只有一个元素
5. 把out中的pop出来,即是原队列的最后一个
tip:这不能像栈实现队列一样,因为另一个queue也是FIFO,如果执行pop()它不能像
stack一样从另一个pop(),所以干脆in只用来存数据,pop()的时候两个进行交换
"""
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 # 交换in和out,这也是为啥in只用来存
return self.queue_out.popleft()
def top(self) -> int:
"""
写法一:
1. 首先确认不空
2. 我们仅有in会存放数据,所以返回第一个即可(这里实际上用到了栈)
写法二:
1. 首先确认不空
2. 因为队列的特殊性,FIFO,所以我们只有在pop()的时候才会使用queue_out
3. 先把queue_in中的所有元素(除了最后一个),依次出列放进queue_out
4. 交换in和out,此时out里只有一个元素
5. 把out中的pop出来,即是原队列的最后一个,并使用temp变量暂存
6. 把temp追加到queue_in的末尾
"""
# 写法一:
# if self.empty():
# return None
# return self.queue_in[-1] # 这里实际上用到了栈,因为直接获取了queue_in的末尾元素
# 写法二:
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
temp = self.queue_out.popleft()
self.queue_in.append(temp)
return temp
def empty(self) -> bool:
"""
因为只有in存了数据,只要判断in是不是有数即可
"""
return len(self.queue_in) == 0