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.
Example:
MyStack stack = new MyStack(); stack.push(1); stack.push(2); stack.top(); // returns 2 stack.pop(); // returns 2 stack.empty(); // returns false
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).
Credits:
Special thanks to @jianchao.li.fighter for adding this problem and all test cases.
這道題讓我們用隊列來實現棧,隊列和棧作為兩種很重要的數據結構,它們最顯著的區別就是,隊列是先進先出,而棧是先進后出。題目要求中又給定了限制條件只能用 queue 的最基本的操作,像 back() 這樣的操作是禁止使用的。那么怎么樣才能讓先進先出的特性模擬出先進后出呢,這里就需要另外一個隊列來輔助操作,我們總共需要兩個隊列,其中一個隊列用來放最后加進來的數,模擬棧頂元素。剩下所有的數都按順序放入另一個隊列中。當 push() 操作時,將新數字先加入模擬棧頂元素的隊列中,如果此時隊列中有數字,則將原本有的數字放入另一個隊中,讓新數字在這隊中,用來模擬棧頂元素。當 top() 操作時,如果模擬棧頂的隊中有數字則直接返回,如果沒有則到另一個隊列中通過平移數字取出最后一個數字加入模擬棧頂的隊列中。當 pop() 操作時,先執行下 top() 操作,保證模擬棧頂的隊列中有數字,然后再將該數字移除即可。當 empty() 操作時,當兩個隊列都為空時,棧為空。代碼如下:
解法一:
class MyStack { public: MyStack() {} void push(int x) { q2.push(x); while (q2.size() > 1) { q1.push(q2.front()); q2.pop(); } } int pop() { int x = top(); q2.pop(); return x; } int top() { if (q2.empty()) { for (int i = 0; i < (int)q1.size() - 1; ++i) { q1.push(q1.front()); q1.pop(); } q2.push(q1.front()); q1.pop(); } return q2.front(); } bool empty() { return q1.empty() && q2.empty(); } private: queue<int> q1, q2; };
這道題還有另一種解法,可以參見另一道類似的題 Implement Queue using Stacks,我個人來講比較偏愛下面這種方法,比較好記,只要實現對了 push() 函數,后面三個直接調用隊列的函數即可。這種方法的原理就是每次把新加入的數插到前頭,這樣隊列保存的順序和棧的順序是相反的,它們的取出方式也是反的,那么反反得正,就是我們需要的順序了。我們可以使用一個輔助隊列,把q的元素也逆着順序存入到輔助隊列中,此時加入新元素x,再把輔助隊列中的元素存回來,這樣就是我們要的順序了。當然,我們也可以直接對隊列q操作,在隊尾加入了新元素x后,將x前面所有的元素都按順序取出並加到隊列到末尾,這樣下次就能直接取出x了,符合棧到后入先出到特性,其他三個操作也就直接調用隊列的操作即可,參見代碼如下:
解法二:
class MyStack { public: MyStack() {} void push(int x) { q.push(x); for (int i = 0; i < (int)q.size() - 1; ++i) { q.push(q.front()); q.pop(); } } int pop() { int x = q.front(); q.pop(); return x; } int top() { return q.front(); } bool empty() { return q.empty(); } private: queue<int> q; };
討論:上面兩種解法對於不同的輸入效果不同,解法一花在 top() 函數上的時間多,所以適合於有大量 push() 操作,而 top() 和 pop() 比較少的輸入。而第二種解法在 push() 上要花大量的時間,所以適合高頻率的 top() 和 pop(),較少的 push()。兩種方法各有千秋,互有利弊。
類似題目:
參考資料:
https://leetcode.com/problems/implement-stack-using-queues/
https://leetcode.com/problems/implement-stack-using-queues/discuss/62527/A-simple-C%2B%2B-solution