本文转自:http://zhedahht.blog.163.com/blog/static/2541117420073293950662/
题目:某队列的声明如下:
template<typename T> class CQueue
{
public:
CQueue() {}
~CQueue() {}
void appendTail(const T& node); // append a element to tail
void deleteHead(); // remove a element from head
private:
T> m_stack1;
T> m_stack2;
};
分析:从上面的类的声明中,我们发现在队列中有两个栈。因此这道题实质上是要求我们用两个栈来实现一个队列。相信大家对栈和队列的基本性质都非常了解了:栈是一种后入先出的数据容器,因此对队列进行的插入和删除操作都是在栈顶上进行;队列是一种先入先出的数据容器,我们总是把新元素插入到队列的尾部,而从队列的头部删除元素。
我们通过一个具体的例子来分析往该队列插入和删除元素的过程。首先插入一个元素a,不妨把先它插入到m_stack1。这个时候m_stack1中的元素有{a},m_stack2为空。再插入两个元素b和c,还是插入到m_stack1中,此时m_stack1中的元素有{a,b,c},m_stack2中仍然是空的。
这个时候我们试着从队列中删除一个元素。按照队列先入先出的规则,由于a比b、c先插入到队列中,这次被删除的元素应该是a。元素a存储在m_stack1中,但并不在栈顶上,因此不能直接进行删除。注意到m_stack2我们还一直没有使用过,现在是让m_stack2起作用的时候了。如果我们把m_stack1中的元素逐个pop出来并push进入m_stack2,元素在m_stack2中的顺序正好和原来在m_stack1中的顺序相反。因此经过两次pop和push之后,m_stack1为空,而m_stack2中的元素是{c,b,a}。这个时候就可以pop出m_stack2的栈顶a了。pop之后的m_stack1为空,而m_stack2的元素为{c,b},其中b在栈顶。
这个时候如果我们还想继续删除应该怎么办呢?在剩下的两个元素中b和c,b比c先进入队列,因此b应该先删除。而此时b恰好又在栈顶上,因此可以直接pop出去。这次pop之后,m_stack1中仍然为空,而m_stack2为{c}。
从上面的分析我们可以总结出删除一个元素的步骤:当m_stack2中不为空时,在m_stack2中的栈顶元素是最先进入队列的元素,可以pop出去。如果m_stack2为空时,我们把m_stack1中的元素逐个pop出来并push进入m_stack2。由于先进入队列的元素被压到m_stack1的底端,经过pop和push之后就处于m_stack2的顶端了,又可以直接pop出去。
接下来我们再插入一个元素d。我们是不是还可以把它push进m_stack1?这样会不会有问题呢?我们说不会有问题。因为在删除元素的时候,如果m_stack2中不为空,处于m_stack2中的栈顶元素是最先进入队列的,可以直接pop;如果m_stack2为空,我们把m_stack1中的元素pop出来并push进入m_stack2。由于m_stack2中元素的顺序和m_stack1相反,最先进入队列的元素还是处于m_stack2的栈顶,仍然可以直接pop。不会出现任何矛盾。
我们用一个表来总结一下前面的例子执行的步骤:
操作 | m_stack1 | m_stack2 |
append a | {a} | {} |
append b | {a,b} | {} |
append c | {a,b,c} | {} |
delete head | {} | {b,c} |
delete head | {} | {c} |
append d | {d} | {c} |
delete head | {d} | {} |
总结完push和pop对应的过程之后,我们可以开始动手写代码了。参考代码如下:
/*******
MyQueue.h
*******/
#include <stack>
using namespace std;
class MyQueue
{
public:
void push(int value);
int front();
void pop();
void Print();
bool isReallyEmpty();
private:
stack<int> stack1;
stack<int> stack2;
};
/*******
MyQueue.cpp
*******/
#include <stdio.h>
#include "MyQueue.h"
void MyQueue::push(int value)
{
stack1.push(value);
}
int MyQueue::front()
{
if(!stack2.empty())
{
return stack2.top();
}
else if(!stack1.empty())
{
int value;
while(!stack1.empty())
{
value = stack1.top();
stack1.pop();
stack2.push(value);
}
return stack2.top();
}
else
{
printf("queue is empty");
// isEmpty2 = true;
return 0;
}
}
void MyQueue::pop()
{
if(!stack2.empty())
{
stack2.pop();
}
else if(!stack1.empty())
{
int value;
while(!stack1.empty())
{
value = stack1.top();
stack1.pop();
stack2.push(value);
}
stack2.pop();
}
else
{
printf("queue is empty");
// isEmpty2 = true;
}
}
bool MyQueue::isReallyEmpty()
{
if(stack1.empty() && stack2.empty())
return true;
else
return false;
}
void MyQueue::Print()
{
int value;
while(!isReallyEmpty())
{
value = front();
printf("%d ",value);
pop();
}
printf("\n");
}
/*******
main.cpp
*******/
#include <stdio.h>
#include "MyQueue.h"
int main()
{
MyQueue myqueue;
myqueue.push(1);
/* myqueue.push(2);
myqueue.push(3);
myqueue.push(4);
myqueue.push(5);
myqueue.push(6);
myqueue.push(7);
myqueue.push(8);
myqueue.push(9);*/
// myqueue.Print();
//printf("%d\n",myqueue.front());
myqueue.pop();
// myqueue.Print();
myqueue.pop();
myqueue.Print();
return 0;
}
扩展:这道题是用两个栈实现一个队列。反过来能不能用两个队列实现一个栈?如果可以,该如何实现?
解答:用两个队列实现一个栈。我们通过一系列的压入和弹出操作来分析用两个队列模拟一个栈的过程。如下图a所示,我们先往栈内压入一个元素a.由于两个队列现在都是空的,我们可以选择把a插入两个队列的任意一个。部门不妨把a插入queue1。接下来继续往栈内压入b、c两个元素,我们把它们都插入queue1。这个时候queue1包含3个元素a、b、c,其中a位于队列的头部,c位于队列的尾部。
现在我们考虑从栈内弹出一个元素。根据栈的后入先出原则,最后被压入的c应该最先被弹出。由于c位于queue1的尾部,为我们每次只能从队列的头部删除元素,因此我们可以先从queue1中依次删除元素a、b并插入到queue2中,再从queue1中删除元素c。这就相当于从栈中弹出元素c了,如下图b所示。我们可以用同样的方法从栈内弹出元素b,如图c所示。
接下来我们考虑往栈内压入一个元素d。此时queue已经有一个元素,我们就把d插入到queue1的尾部如下图d所示。如果我们在从栈内弹出一个元素,此时被弹出的应该是最后被压入的d。由于d位于queue1的尾部,我们只能先从头删除queue1的元素并插入到queue2,知道queue1中遇到d再直接把他删除,如下图e所示。
代码如下:
/*********
MyStack.h
*********/
#include <queue>
class MyStack
{
public:
MyStack()
{
flag = 0;//初始queue0为当前队列
}
void Push(int number);
void Pop();
int Top();
bool Empty();
void Print();
private:
int flag;//表示当前的大部分数据在那个队列中,记当前队列为包含大部分数据的队列
std::queue<int> queue0;
std::queue<int> queue1;
};
/*********
MyStack.cpp
*********/
#include <iostream>
#include "MyStack.h"
void MyStack::Push(int number)
{
//将数据放入当前队列中
if(0 == flag)
queue0.push(number);
else
queue1.push(number);
}
void MyStack::Pop()
{
if(!Empty())
{
int number;
//如果queue0为当前队列,便牵入queue0的数据到queue1中,直到queue0中只剩一个数据,然后queue0.Pop()这个数据
if(0 == flag)
{
while(queue0.size() > 1)
{
number = queue0.front();
queue1.push(number);
queue0.pop();
}
if(1 == queue0.size())
{
queue0.pop();
}
flag = 1;//设置当前队列为包含大部分数据的queue1
}
else
{//如果queue1为当前队列,便牵入queue1的数据到queue0中,直到queue1中只剩一个数据,然后queue1.Pop()这个数据
while(queue1.size() > 1)
{
number = queue1.front();
queue0.push(number);
queue1.pop();
}
if(1 == queue1.size())
queue1.pop();//设置当前队列为包含大部分数据的queue0
flag = 0;
}
}
else
std::cout<<"Empty Stack"<<std::endl;
}
bool MyStack::Empty()
{
return (queue0.empty() && queue1.empty());
}
//实现方式同Pop()函数
int MyStack::Top()
{
if(!Empty())
{
int number;
if(0 == flag)
{
while(queue0.size() > 1)
{
number = queue0.front();
queue1.push(number);
queue0.pop();
}
if(1 == queue0.size())
{
return queue0.front();
}
flag = 1;
}
else
{
while(queue1.size() > 1)
{
number = queue1.front();
queue0.push(number);
queue1.pop();
}
if(1 == queue1.size())
return queue1.front();
flag = 0;
}
}
else
std::cout<<"Empty Stack"<<std::endl;
}
//Print()函数会销毁栈中的数据,这里Print函数只为了测试程序的功能使用
void MyStack::Print()
{
int number;
while(!Empty())
{
number = Top();
Pop();
std::cout<<number<<" ";
}
std::cout<<std::endl;
}
/*********
main.cpp
*********/
#include <iostream>
#include "MyStack.h"
using namespace std;
//测试函数
int main()
{
MyStack mystack;
mystack.Push(1);
mystack.Push(2);
mystack.Push(3);
mystack.Push(4);
mystack.Push(5);
/* int number = mystack.Top();
cout<<number<<endl;
mystack.Print();*/
mystack.Pop();
mystack.Pop();
mystack.Pop();
mystack.Pop();
mystack.Pop();
if(!mystack.Empty())
{
int number = mystack.Top();
cout<<number<<endl;
mystack.Print();
}
return 0;
}