假設咱們有兩個堆棧而沒有其餘臨時變量。 java
是否能夠僅使用兩個堆棧「構造」隊列數據結構? node
public class QueueUsingStacks<T> { private LinkedListStack<T> stack1; private LinkedListStack<T> stack2; public QueueUsingStacks() { stack1=new LinkedListStack<T>(); stack2 = new LinkedListStack<T>(); } public void Copy(LinkedListStack<T> source,LinkedListStack<T> dest ) { while(source.Head!=null) { dest.Push(source.Head.Data); source.Head = source.Head.Next; } } public void Enqueue(T entry) { stack1.Push(entry); } public T Dequeue() { T obj; if (stack2 != null) { Copy(stack1, stack2); obj = stack2.Pop(); Copy(stack2, stack1); } else { throw new Exception("Stack is empty"); } return obj; } public void Display() { stack1.Display(); } }
對於每一個enqueue操做,咱們添加到stack1的頂部。 對於每一個dequeue,咱們將stack1的內容清空到stack2中,並刪除堆棧頂部的元素。時間複雜度爲出隊的O(n),由於咱們必須將stack1複製到stack2。 enqueue的時間複雜度與常規堆棧相同 數據結構
and . 隊列中的兩個堆棧定義爲和 。 app
入 :通過排隊的元素老是被推入 ide
can be popped out since it is the first element inserted into queue when is not empty. Dequeue: 的頂部能夠彈出,由於它是不爲空時插入隊列的第一個元素。 is empty, we pop all elements from and push them into one by one. 當爲空時,咱們從彈出全部元素並將它們推入 。 . 隊列中的第一個元素被推入的底部。 . 彈出和推送操做後,它能夠直接彈出,由於它位於的頂部。 spa
如下是相同的C ++示例代碼: code
template <typename T> class CQueue { public: CQueue(void); ~CQueue(void); void appendTail(const T& node); T deleteHead(); private: stack<T> stack1; stack<T> stack2; }; template<typename T> void CQueue<T>::appendTail(const T& element) { stack1.push(element); } template<typename T> T CQueue<T>::deleteHead() { if(stack2.size()<= 0) { while(stack1.size()>0) { T& data = stack1.top(); stack1.pop(); stack2.push(data); } } if(stack2.size() == 0) throw new exception("queue is empty"); T head = stack2.top(); stack2.pop(); return head; }
這個解決方案來自個人博客 。 個人博客網頁上提供了有關逐步操做模擬的更詳細分析。 隊列
讓隊列實現爲q,用於實現q的棧是stack1和stack2。 內存
q能夠經過兩種方式實現: element
方法1(經過使enQueue操做成本高昂)
此方法確保新輸入的元素始終位於堆棧1的頂部,所以deQueue操做只是從stack1彈出。 要將元素放在stack1的頂部,使用stack2。
enQueue(q, x) 1) While stack1 is not empty, push everything from stack1 to stack2. 2) Push x to stack1 (assuming size of stacks is unlimited). 3) Push everything back to stack1. deQueue(q) 1) If stack1 is empty then error 2) Pop an item from stack1 and return it.
方法2(經過使deQueue操做成本高昂)
在此方法中,在隊列操做中,新元素輸入stack1的頂部。 在去隊列操做中,若是stack2爲空,則將全部元素移動到stack2,最後返回stack2的頂部。
enQueue(q, x) 1) Push x to stack1 (assuming size of stacks is unlimited). deQueue(q) 1) If both stacks are empty then error. 2) If stack2 is empty While stack1 is not empty, push everything from stack1 to stack2. 3) Pop the element from stack2 and return it.
方法2確定比方法1好。方法1在enQueue操做中移動全部元素兩次,而方法2(在deQueue操做中)移動元素一次並僅在stack2爲空時移動元素。
// Two stacks s1 Original and s2 as Temp one private Stack<Integer> s1 = new Stack<Integer>(); private Stack<Integer> s2 = new Stack<Integer>(); /* * Here we insert the data into the stack and if data all ready exist on * stack than we copy the entire stack s1 to s2 recursively and push the new * element data onto s1 and than again recursively call the s2 to pop on s1. * * Note here we can use either way ie We can keep pushing on s1 and than * while popping we can remove the first element from s2 by copying * recursively the data and removing the first index element. */ public void insert( int data ) { if( s1.size() == 0 ) { s1.push( data ); } else { while( !s1.isEmpty() ) { s2.push( s1.pop() ); } s1.push( data ); while( !s2.isEmpty() ) { s1.push( s2.pop() ); } } } public void remove() { if( s1.isEmpty() ) { System.out.println( "Empty" ); } else { s1.pop(); } }
我會在Go中回答這個問題,由於Go在其標準庫中沒有豐富的集合。
因爲堆棧實際上很容易實現,我想我會嘗試使用兩個堆棧來完成雙端隊列。 爲了更好地理解我是如何得出答案的,我將實現分爲兩部分,第一部分但願更容易理解,但它不完整。
type IntQueue struct { front []int back []int } func (q *IntQueue) PushFront(v int) { q.front = append(q.front, v) } func (q *IntQueue) Front() int { if len(q.front) > 0 { return q.front[len(q.front)-1] } else { return q.back[0] } } func (q *IntQueue) PopFront() { if len(q.front) > 0 { q.front = q.front[:len(q.front)-1] } else { q.back = q.back[1:] } } func (q *IntQueue) PushBack(v int) { q.back = append(q.back, v) } func (q *IntQueue) Back() int { if len(q.back) > 0 { return q.back[len(q.back)-1] } else { return q.front[0] } } func (q *IntQueue) PopBack() { if len(q.back) > 0 { q.back = q.back[:len(q.back)-1] } else { q.front = q.front[1:] } }
它基本上是兩個堆棧,咱們容許堆棧的底部彼此操縱。 我還使用了STL命名約定,其中堆棧的傳統push,pop,peek操做具備前/後前綴,不管它們是指隊列的前面仍是後面。
上述代碼的問題在於它不能很是有效地使用內存。 實際上,它會不斷增加,直到你的空間不足爲止。 那真的很糟糕。 對此的修復是儘量簡單地重用堆棧空間的底部。 咱們必須引入一個偏移來跟蹤這個,由於Go中的切片一旦收縮就不能在前面生長。
type IntQueue struct { front []int frontOffset int back []int backOffset int } func (q *IntQueue) PushFront(v int) { if q.backOffset > 0 { i := q.backOffset - 1 q.back[i] = v q.backOffset = i } else { q.front = append(q.front, v) } } func (q *IntQueue) Front() int { if len(q.front) > 0 { return q.front[len(q.front)-1] } else { return q.back[q.backOffset] } } func (q *IntQueue) PopFront() { if len(q.front) > 0 { q.front = q.front[:len(q.front)-1] } else { if len(q.back) > 0 { q.backOffset++ } else { panic("Cannot pop front of empty queue.") } } } func (q *IntQueue) PushBack(v int) { if q.frontOffset > 0 { i := q.frontOffset - 1 q.front[i] = v q.frontOffset = i } else { q.back = append(q.back, v) } } func (q *IntQueue) Back() int { if len(q.back) > 0 { return q.back[len(q.back)-1] } else { return q.front[q.frontOffset] } } func (q *IntQueue) PopBack() { if len(q.back) > 0 { q.back = q.back[:len(q.back)-1] } else { if len(q.front) > 0 { q.frontOffset++ } else { panic("Cannot pop back of empty queue.") } } }
這是不少小功能,但其中有6個功能,其中3個只是另外一個的鏡像。