LeetCode[Stack]----Min Stack&Stack&Queue
整理一下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