简单的用栈实现队列
队列是一种先进先出的数据结构,栈是一种先进后出的数据结构,形象一点就是这样:
在面试中常常会问到这样一个问题:怎么使用栈来实现一个队列?如何用队列来实现一个栈?
前者问的较多,后者问的较少,但掌握得多也不是坏事嘛,接下来我们就来解析下这两个问题。
用栈实现队列
首先,队列的 API 如下:
class MyQueue {
/** 添加元素到队尾 */
public void push(int x);
/** 删除队头的元素并返回 */
public int pop();
/** 返回队头元素 */
public int peek();
/** 判断队列是否为空 */
public boolean empty();
}
我们使用两个栈s1, s2
就能实现一个队列的功能。
class MyQueue {
private Stack<Integer> s1, s2;
public MyQueue() {
s1 = new Stack<>();
s2 = new Stack<>();
}
// ...
}
首先来解决入队的问题,当调用push
让元素入队时,只要把元素压入s1
即可:
/** 添加元素到队尾 */
public void push(int x) {
s1.push(x);
}
那么使用peek
查看队头的元素怎么办呢?按道理队头元素应该是 1,但是在s1
中 1 被压在栈底,现在就要轮到s2
起到一个中转的作用了:
当s2
为空时,可以把s1
的所有元素取出再添加进s2
,这时候s2
中元素就是先进先出顺序了。
/** 返回队头元素 */
public int peek() {
if (s2.isEmpty())
// 把 s1 元素压入 s2
while (!s1.isEmpty())
s2.push(s1.pop());
return s2.peek();
}
同理,对于pop
操作,只要操作s2
就可以了。
/** 删除队头的元素并返回 */
public int pop() {
// 先调用 peek 保证 s2 非空
peek();
return s2.pop();
}
最后,如何判断队列是否为空呢?如果两个栈都为空的话,就说明队列为空:
/** 判断队列是否为空 */
public boolean empty() {
return s1.isEmpty() && s2.isEmpty();
}
至此,就用栈结构实现了一个队列,核心思想是利用两个栈互相配合。
用队列实现栈
首先看下栈的 API:
class MyStack {
/** 添加元素到栈顶 */
public void push(int x);
/** 删除栈顶的元素并返回 */
public int pop();
/** 返回栈顶元素 */
public int top();
/** 判断栈是否为空 */
public boolean empty();
}
有两种方法实现,分别是用两个队列和一个队列的方法。
总的来说,无论是双队列还是单队列实现,都是确保调用push()
方法时,新增的元素在栈顶就行了。
先用两个队列来实现:
class MyStack {
Queue<Integer> queue1;
Queue<Integer> queue2;
public MyStack() {
queue1 = new LinkedList<Integer>();
queue2 = new LinkedList<Integer>();
}
/** 确保queue1的前端元素为栈顶元素 */
public void push(int x) {
// 首先将元素入队到queue2
queue2.offer(x);
// queue1的全部元素依次出队并入队到queue2
// 此时queue2的前端的元素即为新入栈的元素
while (!queue1.isEmpty()) {
queue2.offer(queue1.poll());
}
// 两队列互换
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();
}
}
单队列实现:
class MyStack {
Queue<Integer> queue;
public MyStack() {
queue = new LinkedList<Integer>();
}
/** 确保队列的前端元素为栈顶元素 */
public void push(int x) {
// 获得入栈前元素个数
int n = queue.size();
queue.offer(x);
// 将队列中的前n个元素依次出队并入队到队列
// 此时队列的前端的元素即为新入栈的元素
for (int i = 0; i < n; i++) {
queue.offer(queue.poll());
}
}
public int pop() {
return queue.poll();
}
public int top() {
return queue.peek();
}
public boolean empty() {
return queue.isEmpty();
}
}