我们在之前学习了栈和队列,栈的特性是先进后出,而队列的特性是先进先出。现在有两个有趣的问题:既然栈和队列在实现上非常相似,是否可以用栈实现队列?再者是否可以用队列来实现栈呢?今天我们就来探究下这两个问题。
那么用栈实现队列的核心是什么呢?等价于用“后进先出”的特性实现“先进先出”的特性!解决方案:可以用两个栈进行实现,方案设计如下
实现思路:准备用两个栈来实现队列,stack_in 和 stack_out。
1、当有新元素入队时:将其压入 stack_in;
2、当需要出队时:
a> stack_out.size() == 0 时,将 stack_in 中的元素逐一弹出并压入 stack_out,再将 stack_out 的栈顶元素弹出;
b> stack_out.size() > 0 时,将 stack_out 的栈顶元素弹出。
我们来看看 StackToQueue 的实现
StackToQueue.h 源码
template < typename T > class StackToQueue : public Queue<T> { protected: mutable LinkStack<T> m_stack_in; mutable LinkStack<T> m_stack_out; void move() const // O(n) { if( m_stack_out.size() == 0 ) { while( m_stack_in.size() > 0 ) { m_stack_out.push(m_stack_in.top()); m_stack_in.pop(); } } } public: void add(const T& e) // O(1) { m_stack_in.push(e); } void remove() // O(n) { move(); if( m_stack_out.size() > 0 ) { m_stack_out.pop(); } else { THROW_EXCEPTION(INvalidOPerationException, "No element in current queue ..."); } } T front() const // O(n) { move(); if( m_stack_out.size() > 0 ) { m_stack_out.top(); } else { THROW_EXCEPTION(INvalidOPerationException, "No element in current queue ..."); } } void clear() // O(n) { m_stack_in.clear(); m_stack_out.clear(); } int length() const // O(1) { return m_stack_in.size() + m_stack_out.size(); } };
测试代码如下
int main() { cout << "StackToQueue begin ..." << endl; StackToQueue<int> sq; for(int i=0; i<5; i++) { sq.add(i); } while( sq.length() > 0 ) { cout << sq.front() << endl; sq.remove(); } return 0; }
编译结果如下
我们看到用栈实现了队列的“先进先出”的特性。那么我们下来来用队列实现栈,其本质为用队列“先进先出”的特性实现“后进先出”的特性!解决方案:用两个队列实现,方案设计如下
实现思路:准备用两个队列用于实现栈:queue_1[in] 和 queue_2[out]。
1、当有新元素入栈时:将其加入队列 [in];
2、当需要出栈时:
a> 将队列 [in] 中的 n-1 个元素出队列,并进入队列 [out] 中;
b> 将队列 [in] 中的最后一个元素出队列(出栈) ;
c> 交换两个队列的角色:queue_1 [out] 和 queue_2 [in]。
我们来看看 QueueToStack 的实现
QueueToStack.h 源码
template < typename T > class QueueToStack : public Stack<T> { protected: LinkQueue<T> m_queue_1; LinkQueue<T> m_queue_2; LinkQueue<T>* m_pIn; LinkQueue<T>* m_pOut; void move() const // O(n) { int n = m_pIn->length() -1; for(int i=0; i<n; i++) { m_pOut->add(m_pIn->front()); m_pIn->remove(); } } void swap() // O(1) { LinkQueue<T>* temp = NULL; temp = m_pIn; m_pIn = m_pOut; m_pOut = temp; } public: QueueToStack() // O(1) { m_pIn = &m_queue_1; m_pOut = &m_queue_2; } void push(const T& e) // O(1) { m_pIn->add(e); } void pop() // O(n) { if( m_pIn->length() > 0 ) { move(); m_pIn->remove(); swap(); } else { THROW_EXCEPTION(INvalidOPerationException, "No element in curruent stack ..."); } } T top() const // O(n) { if( m_pIn->length() > 0 ) { move(); return m_pIn->front(); } else { THROW_EXCEPTION(INvalidOPerationException, "No element in curruent stack ..."); } } void clear() // O(n) { m_queue_1.clear(); m_queue_2.clear(); } int size() const // O(1) { return m_queue_1.length() + m_queue_2.length(); } };
测试代码如下
int main() { cout << "QueueToStack begin ..." << endl; QueueToStack<int> qs; for(int i=0; i<5; i++) { qs.push(i); } while( qs.size() > 0 ) { cout << qs.top() << endl; qs.pop(); } return 0; }
我们看看编译结果
我们看到已经用队列实现了栈的“后进先出”特性。通过今天对栈和队列这两个相互转换的学习,对栈和队列的特性掌握的很熟悉了。总结如下:1、栈和队列在实现上非常类似,可以相互转化实现;2、两个栈“后进先出”叠加得到“先进先出”的特性;3、两个队列“先进先出”相互配合得到“后进先出”的特性;4、栈和队列相互转化的学习有助于强化对其本质的理解。