栈和队列:
栈的特点是后进先出(Last In First Out), 它在计算机领域被广泛应用,比如操作系统会给每个线程创建一个栈用来存储函数调用时各个函数的参数、返回地址、临时变量等。
队列的特点是先进先出(First In First Out)。
本文使用两个栈来实现队列。
题目描述:
用两个栈实现一个队列,队列的声明如下,请实现它的两个函数appendTail和deleteHead, 分别完成在队列的尾部插入节点和在队列的头部删除节点的功能。
队列声明:
//队列模板定义
template <typename T>
class Queue{
public:
Queue(){}
~Queue(){}
void appendTail(const T &node);
T deleteHead();
void show()const;
private:
std::stack<T> stack1;
std::stack<T> stack2;
};
举例分析:
在队列的声明中可以看出,一个队列包含了两个栈stack1和stack2;首先向stack1插入元素a,b,c; stack1 = {c, b, a}; c是栈顶元素。此时我们想以队列的方式删除队列头部节点,即a元素。 作为栈是后进先出的原则,我们是无法直接在stack1中删除a的。 但我们有一个stack2可以使用。依次弹出stack1元素并push进stack2, 此时stack2 = {a, b, c}; a为栈顶元素, 然后对stack2执行pop操作即会删除a元素, 即作为队列的头元素删除。
在队列的尾部加入元素,仍以上为例。经过头部删除后stack2 = {b, c}; 插入元素时,我们依然是向stack1插入。若想继续删除元素,其中的原理是:只要stack2不为空,我们就可以继续对stack2执行pop;否则,若stack1不为空,我们就将stack1的元素转移到stack2,这样删除元素时始终遵守的是先进先出的原则。
测试用例:
int main(){
Queue<int> queue;
//尾部加入数字
queue.appendTail(4);
queue.appendTail(5);
queue.appendTail(6);
queue.appendTail(7);
queue.appendTail(8);
//输出队列中的所有元素(先进先出),输出顺序应为:4, 5, 6, 7, 8
queue.show();
//删除队首元素(即头部元素)
queue.deleteHead();
//输出队列中的所有元素,验证: 5, 6, 7, 8
queue.show();
//再加入一个元素9
queue.appendTail(9);
//输出:9, 5, 6, 7, 8
queue.show();
return 0;
}
模板类方法实现:
template <typename T>
void Queue<T>::appendTail(const T &node){
stack1.push(node);
}
template <typename T>
T Queue<T>::deleteHead(){
//根据之前的分析,要删除元素是从stack2中删除.所以需要先判断stack2是否有元素
if(stack2.size() <= 0){//若没有,需从stack1中加入
while(stack1.size() > 0){ //当stack1不为空时,才能向stack2中加入元素
T &data = stack1.top(); //取栈顶元素
stack1.pop(); //删除栈顶元素
stack2.push(data);
}
}
//如果这时候stack2仍未0,无论这是第几次删除,都表明队列为空
if(stack2.size() == 0)
throw new std::exception("Queue is empty.");
//否则,删除头元素
T head = stack2.top();
stack2.pop();
return head;
}
//打印队列所有元素
template <typename T>
void Queue<T>::show()const{
std::stack<T> stack3 = stack1;
std::stack<T> stack4 = stack2;
while(stack3.size() > 0){
T &data = stack3.top();
stack3.pop();
stack4.push(data);
}
while(stack4.size() > 0){
std::cout << stack4.top() << " ";
stack4.pop();
}
std::cout << std::endl;
return;
}
总结:
使用两个栈来实现队列,实际上是一个栈用来存储新入队的元素, 一个栈用来维护队首出队元素。