小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
问题描述
用两个栈来实现一个队列,完成 n 次在队列尾部插入整数(push)和在队列头部删除整数(pop)的功能。 队列中的元素为int类型。保证操作合法,即保证pop操作时队列内已有元素。
示例:
输入:["PSH1","PSH2","POP","POP"]
返回值:1,2
说明:
"PSH1":代表将1插入队列尾部 "PSH2":代表将2插入队列尾部 "POP“:代表删除一个元素,先进先出=>返回1 "POP“:代表删除一个元素,先进先出=>返回2
分析问题
首先,我们需要知道队列和栈的区别。
- 队列是一种先进先出的数据结构。队列中的元素是从后端入队,从前端出队。就和排队买票一样。
- 栈是一种后进先出的数据结构。栈中的元素是从栈顶压入,从栈顶弹出。
为了使用两个栈实现队列的先进先出的特性,我们需要用一个栈来反转元素的入队顺序。
入队操作Push:
因为栈是后进先出的,而队列是先进先出的,所以要想使用栈来实现队列的先进先出功能,我们需要把新入栈的元素放入栈底。为了实现这个操作,我们需要先把栈S1中的元素移动到S2,接着再把新来的元素压入S2,然后再把S2中的所有元素再弹出,压入到S1。这样就实现了把新入栈的元素放入栈底的功能。
def push(self, node):
# write code here
while self.stack1:
self.stack2.append(self.stack1.pop())
self.stack2.append(node)
while self.stack2:
self.stack1.append(self.stack2.pop())
出队操作Pop:
我们直接从S1弹出就可以了,因为经过反转后,S1中的栈顶元素就是最先入栈的元素,也就是队首元素。
def pop(self):
if self.stack1:
return self.stack1.pop()
我们来看完整的代码实现。
class Solution:
def __init__(self):
self.stack1 = []
self.stack2 = []
def push(self, node):
# write code here
while self.stack1:
self.stack2.append(self.stack1.pop())
self.stack2.append(node)
while self.stack2:
self.stack1.append(self.stack2.pop())
def pop(self):
if self.stack1:
return self.stack1.pop()
我们可以看到入队操作的时间复杂度是O(n),空间复杂度也是O(n)。出队时间复杂度是O(1),空间复杂度也是O(1)。
优化
在上面的算法中,不知道你有没有发现,每次在push一个新元素时,我们都需要把S1中的元素移动到S2中,然后再从S2移回到S1中。这显然是冗余的。其实,我们在入队时只需要插入到S1中即可。而出队的时候,由于第一个元素被压在了栈S1的底部,要想实现队列的先进先出功能,我们就需要把S1的元素进行反转。我们可以把栈S1的元素Pop出去,然后压入S2。这样就把S1的栈底元素放在了栈S2的栈顶,我们直接从S2将它弹出即可。一旦 S2 变空了,我们只需把 S1 中的元素再一次转移到 S2 就可以了。
下面我们来看一下代码实现。
class Solution:
def __init__(self):
self.stack1 = []
self.stack2 = []
def push(self, node):
# write code here
self.stack1.append(node)
def pop(self):
if not self.stack2:
while self.stack1:
self.stack2.append(self.stack1.pop())
return self.stack2.pop()