最近有人问我这样 一道面试题:请用两个栈实现一个队列的功能。
老实讲这确实是一道非常经典的面试题,我记得我当初找工作参加面试时,就遇到过不止一次。在此总结一下这道题的思路及解法。
首先我们先来看一下怎样用两个栈实现一个队列的功能。栈与队列都是线性结构,都可以用顺序表或链表来实现,其最大的区别在于它们的逻辑特性不同。对于栈来说,它是一个先进后出的线性表,最开始进入栈中的元素总是最后一个出来。而队列恰好相反,它是先进先出的线性表,最开始进入队列的元素一定是第一个出来的。所以我们要用栈实现队列的功能,就必须通过一种方式将先进后出转化为先进先出,这样就能模拟出队列的逻辑特性了。
面对这道题时,很多人都会有这样的思路:
使用两个栈,一个栈(不妨叫做s1)用来存放数据,一个栈(不妨叫做s2)用来作为缓冲区。当入队列操作时,将元素压入栈s1中;当初队列操作时,将s1的元素逐个弹出并压入s2,将s2的顶元素弹出作为出队元素,然后再将s2中的元素逐个弹出并压入s1中。如图所示:
但是该方法存在一些冗余操作。细心的朋友一定会看出,出队列操作的第二步“出栈”完成后,没有必要将栈s2中的数据全部倒回栈s1中,因为下次的出队列操作还要在s2中完成,而且下一次的出队列操作取出的元素仍是当前s2的栈顶元素。因此该算法可以改进一下:
入队操作时,将元素压入栈s1中;出队操作时,判断s2是否为空,如果s2不为空,则直接取出s2的栈顶元素;如果s2为空,则将s1的元素逐个弹出并压入s2,再取出s2的栈顶元素即可。
本题的代码如下:
sqStack s1, s2; /*定义两个栈s1和s2,用它们实现一个队列*/
int initQueue() {
if (initStack(&s1) && initStack(&s2)) {
return 1;
}
return 0;
}
int EnQueue(int x) {
if (Push(&s1,x)) {
return 1; /*入队列,将x压入栈s1*/
}
return 0;
}
int DeQueue(int *x) {
int e;
if (s2.base == s2.top) {
/*栈s2为空的情况*/
if (s1.top == s1.base) {
return 0;
} else {
while (Pop(&s1,&e)) {
Push(&s2,e);
}
}
}
Pop(&s2,x); /*出队列,从栈s2中取出数据*/
return 1;
}
其实本题最好用C++实现,这样定义一个类queue,并在类中实现方法EnQueue,DeQueue等,实现的方式则是通过两个堆栈模拟一个队列的操作。
这样就相当于一个适配器模式,将Class stack封了一层接口,并通过一些算法实现队列的操作。