题目:用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
//队列声明如下
template <typename T> class CQueue
{
public:
CQueue(void) {}
~CQueue(void) {}
void push(const T& node);
T pop();
void printEmu();
private:
stack<T> stack1;
stack<T> stack2;
};
解析:众所周知栈的特性是后进先出,队列的特性先进先出。要想用栈实现队列,必须有两个栈进行。例如把1,2,3存入CQueue队列,将他们按顺序压入stack1,此时stack1是:
3 |
2 |
1 |
栈顶元素为c,若进行出队列操作,应该出队1,但是1在栈底,顺序刚好相反。将stack1中的栈顶依次压入stack2,顺序就会发生该变,此时取stack2的栈顶即为队列队首:
1 | |
2 | |
3 |
2 | |
3 |
我之前的想法是取完队首再将stack2全部压入stack1,维持原始的状态:
3 | |
2 |
关键地方:之前我一直认为,取完队首元素后,stack2中的元素应该全部压入stack1,维持每次一致的状态,即stack1逆序存储队列元素,stack2为空用于辅助pop操作。后来看了书和网上教程之后,发现我的这种想法做法实际效果不是特别好,虽然完全满足需求,但是空间利用率仅为正确解法的一半。
正确的做法应该是:
1.入队永远是压入stack1;
2.取队首元素的时候,将stack1元素压入stack2后,取完元素,就不再将元素压回stack1。换句话说,若stack2不为空那么它是跟队列头顺序一致,且stack2栈顶就是当前队列的队首,若要出队只要stack2出栈即可。当stack2为空的时候才把stack1全部压入stack2,取栈顶即队首。这样不止可以用stack1保存元素,stack2也可以存储,并且减少了存取操作,可谓一举两得。
两个栈都存在元素的情况:
3 |
2 |
1 |
pop():
1 | |
2 | |
3 |
2 | |
3 |
2 | |
4 | 3 |
push(5):
5 | 2 |
4 | 3 |
pop():
5 | |
4 | 3 |
pop():
5 | |
4 |
pop():
5 | |
4 |
4 | |
5 |
5 |
代码:
#include <stack>
#include <iostream>
#include <deque>
using namespace std;
//队列声明如下
template <typename T> class CQueue
{
public:
CQueue(void) {}
~CQueue(void) {}
void push(const T& node);
T pop();
void printEmu();
private:
stack<T> stack1;
stack<T> stack2;
};
template <typename T>
void CQueue<T>::push(const T& node)
{
stack1.push(node);
}
/*不完美做法
template <typename T>
T CQueue<T>::pop()
{
while (!stack1.empty())
{
stack2.push(stack1.top());
stack1.pop();
}
T temp = stack2.top();
stack2.pop();
while (!stack2.empty())
{
stack1.push(stack2.top());
stack2.pop();
}
return temp;
}
*/
//完美做法
template <typename T>
T CQueue<T>::pop()
{
//如果stack2为空,说明只有stack1保存元素且为逆序
if (stack2.empty())
{
//将stack1全部压入stack2,使改变顺序
while (!stack1.empty())
{
stack2.push(stack1.top());
stack1.pop();
}
}
//无论如何,此时stack2栈顶即队首
T res = stack2.top();
stack2.pop();
return res;
}
//顺序输出队列元素,用于测试,可能会改变原队列存储结构,内容不变
template <typename T>
void CQueue<T>::printEmu()
{
if (stack1.empty() && stack2.empty())
{
cout << "空队列" << endl;
return;
}
deque<T> record;
//stack2不为空优先输出stack2
while(!stack2.empty())
{
cout << stack2.top() << " ";
record.push_back(stack2.top());
stack2.pop();
}
while (!stack1.empty())
{
stack2.push(stack1.top());
stack1.pop();
}
while (!stack2.empty())
{
cout << stack2.top() << " ";
record.push_back(stack2.top());
stack2.pop();
}
cout << endl;
//输出结束后将队列元素重新入队
while (!record.empty())
{
stack1.push(record.front());
record.pop_front();
}
}
//测试
int main()
{
CQueue<int> myQue;
for (int i = 0; i < 10; ++i)
{
myQue.push(i);
}
myQue.printEmu();
myQue.pop();
myQue.printEmu();
system("pause");
return 0;
}
值得一提的是,在STL中,栈stack使用双端队列deque实现的,这道题有点逆流而上的幽默感。