整理一下LeetCode上三道与栈有关的题,分别是Implement Queue using Stacks,Implement Stack using Queues和Min Stack。
1.Implement Queue using Stacks
Implement the following operations of a queue using stacks.
-
- push(x) -- Push element x to the back of queue.
- pop() -- Removes the element from in front of queue.
- peek() -- Get the front element.
- empty() -- Return whether the queue is empty.
Notes:
-
- You must use only standard operations of a stack -- which means only
push to top
,peek/pop from top
,size
, andis empty
operations are valid. - Depending on your language, stack may not be supported natively. You may simulate a stack by using a list or deque (double-ended queue), as long as you use only standard operations of a stack.
- You may assume that all operations are valid (for example, no pop or peek operations will be called on an empty queue).
- You must use only standard operations of a stack -- which means only
分析:
用栈来实现队列,要求实现Queue的几个函数,push, pop,peek,empty。这题我曾经写过,代码在这里。
当时用的是两个栈st1和st2模拟一个队列的方式,不过代码可以优化,因为我之前的代码是一直保持着两个栈始终有一个栈存储全部的队列元素。而实际上,在使用两个栈的时候,我们可以只使用其中一个栈如st1作为入栈,接受(push)元素,而另一个栈如st2作为出栈,用来弹出元素(pop)。只有当st2为空的时候,我们才从st1中转移元素到st2中。这种方式相对于我之前的方式减少了栈之间元素转移的次数,提高了效率。
代码为:
class Queue(object):
def __init__(self):
"""
initialize your data structure here.
"""
self.st1 = [] # st1 only input
self.st2 = [] # st2 only output
def push(self, x):
"""
:type x: int
:rtype: nothing
"""
self.st1.append(x)
def pop(self):
"""
:rtype: nothing
"""
if self.st2:
self.st2.pop()
return
self.move()
self.st2.pop()
def peek(self):
"""
:rtype: int
"""
if self.st2:
return self.st2[-1]
self.move()
return self.st2[-1]
def empty(self):
"""
:rtype: bool
"""
if self.st1 or self.st2:
return False
return True
def move(self):
while self.st1:
self.st2.append(self.st1.pop())
2.Implement Stack using Queues
Implement the following operations of a stack using queues.
-
- push(x) -- Push element x onto stack.
- pop() -- Removes the element on top of the stack.
- top() -- Get the top element.
- empty() -- Return whether the stack is empty.
Notes:
-
- You must use only standard operations of a queue -- which means only
push to back
,peek/pop from front
,size
, andis empty
operations are valid. - Depending on your language, queue may not be supported natively. You may simulate a queue by using a list or deque (double-ended queue), as long as you use only standard operations of a queue.
- You may assume that all operations are valid (for example, no pop or top operations will be called on an empty stack).
- You must use only standard operations of a queue -- which means only
分析:
用队列来实现栈。我们知道队列是先进先出的,而栈是先进后出的。要实现这种模拟,我们可以一种思路上非常简单的双队列法。双队列法的思路是,给定两个队列que1和que2,每次保证que1中存储所有的栈元素,然后push时,将元素push到que1中;pop时,将que1中的n-1个元素转移到que2中,并删除que1中最后一个元素。这种思路虽然简单,但是实现会比较麻烦而且效率较低,每次都需要转移两个队列并且还需要计数。
我们可以换一种思路想,如果队列能够让逆序存储每次输入的元素,那么就可以用这个队列来模拟栈了。比如,输入是[1, 2, 3, 4],对应的队列变化情况应该是,输入1对应的queue为:[1];输入2时对应的queue为:[2, 1];输入3时对应的queue为:[3, 2, 1];输入4时对应的queue为:[4, 3, 2, 1]。这样,能够满足出栈时元素为队列的第一个元素,不需要进行计数了。而实现上述逆序存储的方法,可以在队列每加入一个新元素时,弹出队列中的所有元素重新加入到队列中。
代码为:
class Stack(object):
def __init__(self):
"""
initialize your data structure here.
"""
self.que = [] # 用list模拟queue,出队列为self.que.pop(0)
def push(self, x):
"""
:type x: int
:rtype: nothing
"""
self.que.append(x)
for i in range(len(self.que) - 1):
self.que.append(self.que.pop(0))
def pop(self):
"""
:rtype: nothing
"""
self.que.pop(0)
def top(self):
"""
:rtype: int
"""
return self.que[0]
def empty(self):
"""
:rtype: bool
"""
if self.que:
return False
return True
3.Min Stack
Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.
-
- push(x) -- Push element x onto stack.
- pop() -- Removes the element on top of the stack.
- top() -- Get the top element.
- getMin() -- Retrieve the minimum element in the stack.
分析:
设计一个栈,使得栈支持O(1)时间内进行push,pop,top以及getMin(获取栈中最小元素)的方法。这里,如何设计让栈支持O(1)时间获取最小元素是问题的关键。显然,如果只使用一个变量存储最小值,则在有元素弹出时无法进行O(1)时间的更新,如果额外使用最小堆存储,也无法进行O(1)时间更新。这里我们可以使用一个额外的栈来存储当前位置的最小元素。
我们使用两个栈st和minst来分别存储栈元素和对应的最小元素。当有输入[5, 7, 2, 1]时,st存储的为[5, 7, 2, 1],而minst存储的为[5, 5, 2, 1]。让st和minst每次同步操作即可随时获取当前的最小元素值。
代码为:
class MinStack(object):
def __init__(self):
"""
initialize your data structure here.
"""
self.st = []
self.minst = []
def push(self, x):
"""
:type x: int
:rtype: void
"""
if self.st:
if x < self.minst[-1]:
self.minst.append(x)
else:
self.minst.append(self.minst[-1])
else:
self.minst.append(x)
self.st.append(x)
def pop(self):
"""
:rtype: void
"""
self.st.pop()
self.minst.pop()
def top(self):
"""
:rtype: int
"""
return self.st[-1]
def getMin(self):
"""
:rtype: int
"""
return self.minst[-1]
改进:
上面的代码用了两个栈进行存储,在存储空间不变的情况下,可以只使用一个栈进行存储。反正两个栈的数据push和pop都是同步操作的,那么可以只使用st进行存储当前位置元素和当前位置对应的最小值元素。代码略。
再改进:
上面说的使用一个栈进行数据存储,但是空间占用跟两个栈是一样的。有一种跟上面一样只使用一个栈,但是占用更少空间的方法。我们用一个栈st来存储元素和对应的最小值,但是只在新来的数据x小于等于最小值时同时在栈中记录上一次的最小值,然后更改本次的最小值为x。比如有输入[5, 7, 8, 2],设置当前最小值为MAXINT,输入5,小于MAXINT,则st存储当前值和上一次的最小值,st为[MAXINT, 5],同时修改最小值为5;输入7时,至存储7,st为[MAXINT, 5, 7];同理,存储8,st为[MAXINT, 5, 7, 8];最后遇到2时,st为[MAXINT, 5, 7, 8, 5, 2]。弹栈时,如果当前元素与记录的当前最小值相同,则两次弹栈,同时修改当前最小值;如果不相同,则只弹栈一次,不修改最小值。
代码为:
class MinStack(object):
def __init__(self):
"""
initialize your data structure here.
"""
self.minval = float('inf')
self.st = []
def push(self, x):
"""
:type x: int
:rtype: void
"""
if x < self.minval:
self.st.append(self.minval)
self.minval = x
self.st.append(x)
def pop(self):
"""
:rtype: void
"""
if self.st[-1] == self.minval:
self.st.pop()
self.minval = self.st[-1]
self.st.pop()
def top(self):
"""
:rtype: int
"""
return self.st[-1]
def getMin(self):
"""
:rtype: int
"""
return self.minval