做题之前,我们先来回顾一下“栈和队列的相同点以及不同点”,便于做题时的应用!
1.区别与联系
相同点:(1)栈和队列都是控制访问点的线性表;
(2)栈和队列都是允许在端点处进行数据的插入和删除的数据结构;
不同点:(1)栈遵循“后进先出(LIFO)”的原则,即只能在该线性表的一头进行数据的插入和删除,该位置称为“栈顶”, 而另外一头称为“栈底”;根据该特性,实现栈时用顺序表比较好;
(2)队列遵循“先进先出(FIFO)”的原则,即只能在队列的尾部插入元素,头部删除元素。根据该特性,在实现队 列时用链表比较好
2.应用场景
栈:括号匹配;用于计算后缀表达式,等数据结构中
队列:应用于类似现实生活中一些排队问题,例如Linux中我们学习进程间通信的时候,有一种通信方式就是“消息队列”等。
下面我们来看问题:
一、用两个栈实现队列
看到这个题,我们就要想到栈和队列的不同,所谓用两个栈实现一个队列是指,我们要实现队列的“尾插”和“头删”操作。
首先,假如我们要插入一些数据“abcd”,我们知道按照这个顺序队列出现的结果也是“abcd”,而栈会出现“dcba”,刚好相反,因此将该栈的到的数据在插入另外一个栈中就会出现我们想要的结果。因此,我们定义两个栈为“stack1”和“stack2”,栈1只用来插入数据,栈2用来删除数据栈1插入进来的数据。
通过下面的图,我们来分析一下这个模拟的场景
图(1):将队列中的元素“abcd”压入stack1中,此时stack2为空;
图(2):将stack1中的元素pop进stack2中,此时pop一下stack2中的元素,就可以达到和队列删除数据一样的顺序了;
图(3):可能有些人很疑惑,就像图3,当stack2只pop了一个元素a时,satck1中可能还会插入元素e,这时如果将stack1中的元素e插入stack2中,在a之后出栈的元素就是e了,显然,这样想是不对的,我们必须规定当stack2中的元素pop完之后,也就是satck2为空时,再插入stack1中的元素。
代码实现如下:
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); } } T head = stack2.top(); stack2.pop(); if (stack2.size() == 0)//当stack2为空时,抛异常 throw new exception("queue is empty"); return head; }
二、用两个队列实现一个栈
由于栈的性质是“后进先出”的,用两个队列模拟实现栈的时候就需要两个队列的元素“互捣”,从而实现栈的这一特性
具体的做法,我们还是用一张图来看一看
图(1):当栈里面插入元素“abcd”的时候,元素a在栈底(最后出去),d在栈顶(最先出去);
图(2):将元素“abc”从q1中头删,然后再q2中尾插进来之后,头删q1中的元素“d”,就相当于实现了栈顶元素的出栈;
图(3):同理,将元素“ab”从q2中头删,然后尾插到q1中,然后再头删q2中的元素“c”;
图(4):同理,删除元素“b”;
图(5):当栈又插入一个元素“e”时,此时元素“a”不能从队列中删除,而是将元素“a”插入q2中,再删除q1中的元素“e”,最后再删除元素“a”。
说明:其中红色框代表该队列中的元素出队列,该队列为空。
代码实现如下:
template<typename T> class CStack { public: CStack(void); ~CStack(void); void appendTail(const T& node); T deleteHead(); private: queue<T> q1; queue<T> q2; }; template<typename T> void CStack<T>::appendTail(const T& node)//实现栈元素的插入 { //数据的插入原则:保持一个队列为空,一个队列不为空,往不为空的队列中插入元素 if (!q1.empty()) { q1.push(node); } else { q2.push(node); } } template<typename T> T CStack<T>::deleteHead()//实现栈元素的删除 { int ret = 0; if (!q1.empty()) { int num = q1.size(); while (num > 1) { q2.push(q1.front()); q1.pop(); --num; } ret = q1.front(); q1.pop(); } else { int num = q2.size(); while (num > 1) { q1.push(q2.front()); q2.pop(); --num; } ret = q2.front(); q2.pop(); } return ret; }