假设咱们有两个堆栈而没有其余临时变量。 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个只是另外一个的镜像。