在《劍指Offer》中,在棧和隊列習題中,作者留下來一道題目供讀者自己實現,即“用兩個隊列實現一個棧”。
在計算機數據結構中,棧的特點是后進先出,即最后被壓入(push)棧的元素會第一個被彈出(pop);隊列的特點是先進先出,即第一個進入隊列的元素將會被第一個彈出來。雖然棧和隊列特點是針鋒相對,但是兩者卻相互聯系,可以互相轉換。
在“用兩個隊列實現一個棧”問題中,我們用兩個隊列的壓入和彈出來模擬棧的壓入和彈出。我們通過畫圖的手段把抽象的問題形象化。
在上圖中,我們先往棧內壓入一個元素a。由於兩個隊列現在都是空,我們可以選擇把a插入兩個隊列中的任一個。我們不妨把a插入queue1,接下來繼續網棧內壓入b,c兩個元素。我們把它們都插入queue1。這個時候 queue1包含3個元素a,b,c,其中a位於隊列的頭部,c位於隊列的尾部。
現在我們考慮從棧內彈出一個元素。根據棧的后入先出的原則,最后被壓入棧的c應該最先被彈出。由於c位於queue1的尾部,而我們每次只能從隊列的頭部刪除元素,因此我們可以從queueu中依次刪除a、b並插入到queue2中,再從queue1中刪除c。這就相當於從棧中彈出元素c了。我們可以用同樣的方法從棧內彈出元素b。
接下來我們考慮從棧內壓入一個元素d.此時queue1已經有了一個元素,我們就把d插入到queue1的尾部。如果我們再從棧內彈出一個元素,此時被彈出的應該是最后被壓入的d.由於d位於queue1的尾部,我們只能先從頭部刪除queue1的元素並插入到queue2,直到queue1中遇到d再直接把它刪除。
注意:在上述思路中,兩個隊列存在全空隊列和一個空隊列的情況。所以,在程序中進入子函數第一步,要判斷兩個隊列是否同時不為空(異常),增加程序的魯棒性。另外,空棧刪除元素要報異常,即兩個隊列均為空;
程序實時更新在Github中: https://github.com/wylloong/TinyPrograms/blob/master/Coding%20Interviews/CStacks
https://github.com/wylloong/TinyPrograms/blob/master/Coding%20Interviews/StackwithTwoQueues
************************************************************
C++程序實現:
// 面試題9附加題:用兩個隊列實現棧 // 題目:用兩個隊列實現一個棧。棧的聲明如下,請實現它的兩個函數appendTail // 和deleteHead,分別完成在棧尾部插入結點和在棧頭部刪除結點的功能。 #pragma once #include<queue> #include<exception> using namespace std; template <typename T> class CStack { public: CStack(); ~CStack(); void appendTail(const T& node); T deleteHead(); private: queue<T> queue1; queue<T> queue2; }; template<typename T> CStack<T>::CStack() { } template<typename T> CStack<T>::~CStack() { } // 插入元素 template<typename T> void CStack<T>::appendTail(const T& node) { if (queue1.size() > 0 && queue2.size() > 0) { //異常情況 } else { //插入到非空隊列,如果均為空則插入到queue2中 if (queue1.size() == 0) { queue2.push(node); } else { queue1.push(node); } } } template<typename T> T CStack<T>::deleteHead() { if (queue1.size() == 0 && queue2.size() == 0) { //異常情況 throw new exception("stack is empty"); } T head; if (queue1.size() > 0) { while (queue1.size()>1) { //queue1中的元素依次刪除,並插入到queue2中,其中queue1刪除最后一個元素 //相當於從棧中彈出隊尾元素 T& data = queue1.front(); queue1.pop(); queue2.push(data); } head = queue1.front(); queue1.pop(); } else { while (queue2.size()>1) { //queue2中的元素依次刪除,並插入到queue1中,其中queue2刪除最后一個元素 //相當於從棧中彈出隊尾元素 T& data = queue2.front(); queue2.pop(); queue1.push(data); } head = queue2.front(); queue2.pop(); } return head; }
#include"CStacks.h" // ====================測試代碼==================== void test(char actual, char expected) { if (actual == expected) printf("Test passed.\n"); else printf("Test failed.\n"); } int main(int argc, char* argv[]) { // 后進先出 CStack<char> stack; stack.appendTail('a'); stack.appendTail('b'); stack.appendTail('c'); char head = stack.deleteHead(); test(head, 'c'); head = stack.deleteHead(); test(head, 'b'); stack.appendTail('d'); head = stack.deleteHead(); test(head, 'd'); stack.appendTail('e'); head = stack.deleteHead(); test(head, 'e'); head = stack.deleteHead(); test(head, 'a'); //空棧刪除,觸發異常 //head = stack.deleteHead(); getchar(); return 0; }
