![50cdbd2c2b2f021583aa90dd089c8713.png](https://img-blog.csdnimg.cn/img_convert/50cdbd2c2b2f021583aa90dd089c8713.png)
本文已收录至 Github《小白学算法》系列: https:// github.com/vipstone/alg orith
之前我们讲过《用两个栈实现一个队列》,而今天我们要讲的是「用队列实现栈」,它们都属于常见的面试题,而我们今天要用多种方法来实现队列到栈的“转变”。
老规矩,先来回顾一下栈(Stack)和队列(Queue)的特性和常见方法。
栈是后进先出(LIFO)的数据结构,常见方法如下:
- push():入栈方法,向栈顶添加元素;
- pop():出栈方法,将栈顶的元素移除并返回元素;
- peek():查询栈顶元素,并不会移除元素。
![4331ac763347e4386649da4394b6288b.png](https://img-blog.csdnimg.cn/img_convert/4331ac763347e4386649da4394b6288b.png)
队列是先进先出(FIFO)的数据结构,常见方法如下:
- offer():入队方法,向队尾添加元素;
- poll():出队方法,从队头移除并返回元素;
- peek():查询队头元素,并不会移除元素。
![2f43c30d232c2224987b789c17fa4838.png](https://img-blog.csdnimg.cn/img_convert/2f43c30d232c2224987b789c17fa4838.png)
知道了这些基础内容之后,就来看今天的问题吧。
题目描述
使用队列实现栈的下列操作:
push(x) -- 元素 x 入栈
pop() -- 移除栈顶元素
top() -- 获取栈顶元素
empty() -- 返回栈是否为空
注意:
- 你只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作是合法的;
- 你所使用的语言也许不支持队列。你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可;
- 你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。
LeetCode 225: https:// leetcode-cn.com/problem s/implement-stack-using-queues/
题目解析
这道题的题目很好理解:只需要将先进先出的队列,转换为后进先出的“栈”就可以了,我们可以从多个方向入手来实现此功能,比如使用两个队列插入并交换的方式,或者是一个队列插入再交换的方式,或双端队列的方式来实现此功能,具体实现方法和代码如下。
实现方法 1:两个队列实现栈
之前我们用两个栈实现了一个队列的文章中,主要使用的是「负负得正」的思想,那么当看到此道题时,首先应该想到的是用两个队列来实现一个栈,但这里的实现思路和用栈实现队列的思路又略有不同,因为队列都是先进先出的,我们可以把它理解为「正数」,那么也就不能用「负负得正」的思想了,我们这里使用两个队列来实现栈,主要的操作思路是先将元素插入一个临时队列中,然后再将另一个队列的所有元素插入到临时队列的尾部,从而实现后进先出功能的,具体的实现步骤如下。
步骤一
添加首个元素,入列到临时队列 queue2
:
![dcca54928151d7402c763b3c2fd35cf7.png](https://img-blog.csdnimg.cn/img_convert/dcca54928151d7402c763b3c2fd35cf7.png)
步骤二
因为正式队列中无元素,因此无需将 queue1
中的元素移动到临时队列 queue2
的尾部,直接将临时队列和正式队列互换即可:
![cd277cdf39471487b727e66133e9efa2.png](https://img-blog.csdnimg.cn/img_convert/cd277cdf39471487b727e66133e9efa2.png)
步骤三
添加第二个元素,先入列到临时队列 queue2
:
![e3eb4c4a3c42c9b798abc2e6930e3824.png](https://img-blog.csdnimg.cn/img_convert/e3eb4c4a3c42c9b798abc2e6930e3824.png)
步骤四
再将 queue1
中的元素移动到 queue2
的尾部,如下所示:
![de23741556ac45281b62aa448a900d94.png](https://img-blog.csdnimg.cn/img_convert/de23741556ac45281b62aa448a900d94.png)
步骤五
再将 queue1
和 queue2
进行互换:
![294491a68d85cd287f061d91c53245ae.png](https://img-blog.csdnimg.cn/img_convert/294491a68d85cd287f061d91c53245ae.png)
步骤六
执行出队操作:
![edca1778b5c82ba279caab9751788142.png](https://img-blog.csdnimg.cn/img_convert/edca1778b5c82ba279caab9751788142.png)
![68d998b1033d6719713456b2cfc140d6.png](https://img-blog.csdnimg.cn/img_convert/68d998b1033d6719713456b2cfc140d6.png)
最终效果
![75fedadc0d20c724d40dd42c81db2f9f.png](https://img-blog.csdnimg.cn/img_convert/75fedadc0d20c724d40dd42c81db2f9f.png)
从最终的效果图我们可以看出,通过两个队列已经实现了后进先出的特性,也就是完成了从队列到栈的转换,实现代码如下:
import java.util.Queue;
class MyStack {
Queue<Integer> queue1;
Queue<Integer> queue2;
public MyStack() {
queue1 = new LinkedBlockingQueue();
queue2 = new LinkedBlockingQueue();
}
/**
* 入栈
*/
public void push(int x) {
// 1.入列临时队列二
queue2.offer(x);
// 2.将队列一的所有元素移动到队列二
while (!queue1.isEmpty()) {
queue2.offer(queue1.poll());
}
// 3.队列一和队列二互换
Queue<Integer> temp = queue1;
queue1 = queue2;
queue2 = temp;
}
/**
* 出栈并返回此元素
*/
public int pop() {
return queue1.poll();
}
/**
* 查询栈顶元素
*/
public int top() {
return queue1.peek();
}
/**
* 判断是否为空
*/
public boolean empty() {
return queue1.isEmpty();
}
}
我们在 LeetCode 中提交以上测试代码,执行结果如下:
![c6022bf24bdf7794bee91178b613f2c6.png](https://img-blog.csdnimg.cn/img_convert/c6022bf24bdf7794bee91178b613f2c6.png)
此方法很稳,竟然击败了 100% 的用户。
实现方法 2:一个队列实现栈
那我们可以不可以用一个队列来实现栈呢?答案是肯定的。
我们只需要执行以下两个步骤就可以实现将队列转换为栈了,具体实现步骤如下:
- 将元素入列到队尾;
- 再将除队尾之外的所有元素移除并重写入列。
这样操作之后,最后进入的队尾元素反而变成了队头元素,也就实现了后进先出的功能了,如下图所示。
步骤一
元素 1 入列:
![8c9e6650d37b8cc84fb0d09eae39c814.png](https://img-blog.csdnimg.cn/img_convert/8c9e6650d37b8cc84fb0d09eae39c814.png)
步骤二
元素 2 入列:
![191315ef44e44233b682b29ca0513d59.png](https://img-blog.csdnimg.cn/img_convert/191315ef44e44233b682b29ca0513d59.png)
步骤三
将最后一个元素之前的所有元素,也就是元素 1,出列重新入列:
![3493cdffaa5e20d2d04943ead5ecf5dd.png](https://img-blog.csdnimg.cn/img_convert/3493cdffaa5e20d2d04943ead5ecf5dd.png)
![7d8a31a1d3d79d32be865931d5af2b98.png](https://img-blog.csdnimg.cn/img_convert/7d8a31a1d3d79d32be865931d5af2b98.png)
步骤四
执行出队操作:
![44244491c825935442270ab65064f744.png](https://img-blog.csdnimg.cn/img_convert/44244491c825935442270ab65064f744.png)
最终效果
![75fedadc0d20c724d40dd42c81db2f9f.png](https://img-blog.csdnimg.cn/img_convert/75fedadc0d20c724d40dd42c81db2f9f.png)
以上思路的实现代码如下:
import java.util.Queue;
class MyStack {
Queue<Integer> queue1;
public MyStack() {
queue1 = new LinkedBlockingQueue();
}
/**
* 入栈
*/
public void push(int x) {
// 获取原队列长度(要移动的次数)
int count = queue1.size();
// 将元素放入队尾
queue1.offer(x);
// 将除最后一个元素外,其他的元素重新入队
for (int i = 0; i < count; i++) {
System.out.println("for");
queue1.offer(queue1.poll());
}
}
/**
* 出栈并返回此元素
*/
public int pop() {
return queue1.poll();
}
/**
* 查询栈顶元素
*/
public int top() {
return queue1.peek();
}
/**
* 判断是否为空
*/
public boolean empty() {
return queue1.isEmpty();
}
}
我们把以上代码在 LeetCode 中提交,执行结果如下:
![30035c0ccbe143cf9498bb77f22c3b70.png](https://img-blog.csdnimg.cn/img_convert/30035c0ccbe143cf9498bb77f22c3b70.png)
此方法依旧很稳,也是同样的击败了 100% 的用户,只不过此方法在内存方面有更好的表现。
实现方法 3:双端队列实现栈
如果觉得以上方法比较难的话,最后我们还有一个更简单的实现方法,我们可以使用 Java 中的双端队列 ArrayDeque
来实现将元素可以插入队头或队尾,同样移除也是,那么这样我们就可以从队尾入再从队尾出,从而就实现了栈的功能了。
双端队列结构如下:
![882b17ca5eb61c40b9c8dde96341ccf4.png](https://img-blog.csdnimg.cn/img_convert/882b17ca5eb61c40b9c8dde96341ccf4.png)
我们来演示一下用双端队列实现栈的具体步骤。
步骤一
元素 1 入队:
![b4a2bf63b137e5e9b341c8b1a0e36735.png](https://img-blog.csdnimg.cn/img_convert/b4a2bf63b137e5e9b341c8b1a0e36735.png)
步骤二
元素 2 入队(队尾):
![74521ac27da206f21c8a34a7bb0c5f07.png](https://img-blog.csdnimg.cn/img_convert/74521ac27da206f21c8a34a7bb0c5f07.png)
步骤三
再从队尾出队:
![9511b4beb0191b50916e2ee8e970a167.png](https://img-blog.csdnimg.cn/img_convert/9511b4beb0191b50916e2ee8e970a167.png)
![a1d7d8b557238d58991e379c6b12c71e.png](https://img-blog.csdnimg.cn/img_convert/a1d7d8b557238d58991e379c6b12c71e.png)
最终效果
![75fedadc0d20c724d40dd42c81db2f9f.png](https://img-blog.csdnimg.cn/img_convert/75fedadc0d20c724d40dd42c81db2f9f.png)
以上思路的实现代码如下:
import java.util.ArrayDeque;
class MyStack {
ArrayDeque<Integer> deque;
public MyStack() {
deque = new ArrayDeque<>();
}
/**
* 入栈
*/
public void push(int x) {
deque.offer(x);
}
/**
* 出栈并返回此元素
*/
public int pop() {
return deque.pollLast();
}
/**
* 查询栈顶元素
*/
public int top() {
return empty() ? -1 : deque.peekLast();
}
/**
* 判断是否为空
*/
public boolean empty() {
return deque.isEmpty();
}
}
我们把以上代码在 LeetCode 中提交,执行结果如下:
![0ae36f58b8514b009c1f0b2d56f43dd7.png](https://img-blog.csdnimg.cn/img_convert/0ae36f58b8514b009c1f0b2d56f43dd7.png)
总结
本文我们用 3 种方法实现了将队列转换为栈,其中最简单的方法是用 Java 中自带的双端队列 ArrayDeque
从队尾入并从队尾出就实现了栈 ,其他两个方法使用的是普通队列,通过入队之后再移动元素到入队元素之后的方法,从而实现了栈的功能。
小伙伴们,你学会了吗?