關於棧和隊列的考察
棧和隊列都是比較常用的數據結構。棧的應用非常的廣泛,比如說,遞歸函數的實現就是借助於棧保存相關的數據。操作系統中每個線程也會使用棧來保存函數調用涉及到的一些參數和其他變量等。棧最大的一個特點就是先進后出(FILO—First-In/Last-Out)。
隊列和棧不同的是,隊列是一種先進先出(FIFO—first in first out)的數據結構。
對應的STL中都有實現了的方法。
棧的相關方法:
入棧,s.push(x) 出棧,s.pop() 訪問棧頂,s.top() 判斷棧空,s.empty() 訪問棧中的元素個數,s.size() |
隊列的方法與棧大同小異,列舉如下:
入隊,q.push(x) 出隊,q.pop() 訪問隊首元素,q.front()、訪問隊尾元素,q.back() 判斷隊列空,q.empty() 訪問隊列中的元素個數,q.size() |
注意,兩者的pop()方法,僅僅刪除棧頂和隊列首元素,並不返回,如需截獲元素,在pop()方法之前使用top()或者front()方法。
面試題1:
用兩個棧實現隊列,請實現兩個函數appendTail和deleteHead,完成在隊列尾部插入結點和在隊列首部刪除結點的功能。模板定義如下:
1 template <typename T> class CQueue 2 { 3 public: 4 CQueue(void); 5 ~CQueue(void); 6 7 // 在隊列末尾添加一個結點 8 void appendTail(const T& node); 9 10 // 刪除隊列的頭結點 11 T deleteHead(); 12 13 private: 14 stack<T> stack1; 15 stack<T> stack2; 16 };
分析:
這道題是要求通過兩個“先進后出”的操作完成“先進先出”的功能。下面這個例子比較形象的給出了實現的過程。
起初的時候,兩個棧都為空,那么只要有元素來,那么默認插入到第一個棧。這是,如果要求刪除一個元素,那么元素已經不在棧頂,在第一個棧中肯定無法直接刪除了,此時我們發現第二個棧還沒有派上用場,這里用到了,把第一個棧中的元素壓入到第二個棧中,可以發現原來在第一個棧中棧底的元素已經出現在第二個棧的棧頂上,所以刪除的功能就實現了。如果這個時候,“隊列”里還有元素,我們還可以繼續出隊,而且,現在要出隊的元素就在第二個棧的棧頂,所以直接出棧即可。
分析到現在,下面給出總結:如果棧2不為空,同時又需要出隊,那么順其自然直接彈出即可。如果棧2為空,那么從棧1中逐個彈出壓入,那么完整的實現了先進先出的功能。
具體的流程和代碼實現如下:
1 template<typename T> void CQueue<T>::appendTail(const T& element) 2 { 3 stack1.push(element); 4 } 5 6 template<typename T> T CQueue<T>::deleteHead() 7 { 8 if(stack2.size()<= 0) 9 { 10 while(stack1.size()>0) 11 { 12 T& data = stack1.top(); 13 stack1.pop(); 14 stack2.push(data); 15 } 16 } 17 18 if(stack2.size() == 0) 19 throw new exception("queue is empty"); 20 21 T head = stack2.top(); 22 stack2.pop(); 23 24 return head; 25 }
面試題2:
用兩個隊列實現棧。
分析:
結合下圖,我們分析一下具體的流程,搞清楚了相關的流程,那么對應的操作就明晰了。
起初的時候,兩個隊列都是空的,那么當“棧”要壓入一個元素,我們就默認將該元素壓入到隊列1中。接下來繼續壓入剩余的元素。
接下來考慮,如果我們想要彈出一個元素如何操作。棧中要求出棧的為棧頂元素,那么即為最后插入的元素,但是該元素在隊列的尾部,必須要求前面的元素出隊后才能訪問,說到這里,你也就發現思路的:出隊前面的元素,到另一個隊列中,那么就可以在原隊列中彈出唯一的元素了。
現在我們再考慮另一個情況,隊列里面還有元素,“棧”又進來了新的元素,那么就將新元素,壓入到存在元素的那一個隊列中,剩余的操作,上面已經提到了,一樣的操作,看圖也許就清晰多了。
1 template <typename T> class CStack 2 { 3 public: 4 CStack(void); 5 ~CStack(void); 6 7 // 在隊列末尾添加一個結點 8 void Input(const T& ); 9 10 // 刪除隊列的頭結點 11 T Output(); 12 13 private: 14 queue<T> queue1; 15 queue<T> queue2; 16 };
1 template<typename T> void CStack<T>::Input(const T& t){ 2 if(queue1.empty()&&queue2.empty()) 3 queue1.push(t); 4 else 5 if(!queue1.empty()) 6 queue1.push(t); 7 else 8 queue2.push(t); 9 } 10 11 template<typename T> T CStack<T>::Output(){ 12 T t; 13 if(queue1.empty()&&queue2.empty()) 14 retutn NULL; 15 else 16 if(queue1.empty()){ 17 while(queue2.size()!=1){ 18 queue1.push(queue2.front()); 19 queue2.pop(); 20 } 21 t=queue2.front(); 22 queue2.pop(); 23 } 24 else 25 { 26 while(queue1.size()!=1){ 27 queue2.push(queue1.front()); 28 queue1.pop(); 29 } 30 t=queue1.front(); 31 queue1.pop(); 32 } 33 return t; 34 } 35