java使用队列实现栈思路_【LeetCode】225.用队列实现栈(三种方法,java实现)

题目

b14ba146beec1d649b2f88b7bd025486.png

方法一 (两个队列,压入 -O(1), 弹出 -O(n)

思路

栈是一种 后进先出(last in - first out, LIFO)的数据结构,栈内元素从顶端压入(push),从顶端弹出(pop)。一般我们用数组或者链表来实现栈,但是这篇文章会来介绍如何用队列来实现栈。队列是一种与栈相反的 先进先出(first in - first out, FIFO)的数据结构,队列中元素只能从 后端(rear)入队(push),然后从 前端(front)端出队(pop)。为了满足栈的特性,我们需要维护两个队列 q1 和 q2。同时,我们用一个额外的变量来保存栈顶元素。

算法

压入(push)

新元素永远从 q1 的后端入队,同时 q1 的后端也是栈的 栈顶(top)元素。

90a7942c5d3ae18ce0661bc9ce079612.png

Figure 1. Push an element in stack

private Queue q1 = new LinkedList<>();

private Queue q2 = new LinkedList<>();

private int top;

// Push element x onto stack.

public void push(int x) {

q1.add(x);

top = x;

}

复杂度分析

时间复杂度:O(1)

队列是通过链表来实现的,入队(add)操作的时间复杂度为 O(1)。

空间复杂度:O(1)

弹出(pop)

我们需要把栈顶元素弹出,就是 q1 中最后入队的元素。

考虑到队列是一种 FIFO 的数据结构,最后入队的元素应该在最后被出队。因此我们需要维护另外一个队列 q2,这个队列用作临时存储 q1 中出队的元素。q2 中最后入队的元素将作为新的栈顶元素。接着将 q1 中最后剩下的元素出队。我们通过把 q1 和 q2 互相交换的方式来避免把 q2 中的元素往 q1 中拷贝。

09d807e527248fa50f985951430d3292.png

Figure 2. Pop an element from stack

// Removes the element on top of the stack.

public void pop() {

while (q1.size() > 1) {

top = q1.remove();

q2.add(top);

}

q1.remove();

Queue temp = q1;

q1 = q2;

q2 = temp;

}

复杂度分析

时间复杂度:O(n)

算法让 q1 中的 n 个元素出队,让 n - 1 个元素从 q2 入队,在这里 n 是栈的大小。这个过程总共产生了 2n - 1 次操作,时间复杂度为 O(n)。

方法二 (两个队列, 压入 - O(n), 弹出 - O(1))

算法

压入(push)

接下来介绍的算法让每一个新元素从 q2 入队,同时把这个元素作为栈顶元素保存。当 q1 非空(也就是栈非空),我们让 q1 中所有的元素全部出队,再将出队的元素从 q2 入队。通过这样的方式,新元素(栈中的栈顶元素)将会在 q2 的前端。我们通过将 q1, q2 互相交换的方式来避免把 q2 中的元素往 q1 中拷贝。

243524a904af9032b2d078a706687a1e.png

Figure 3. Push an element in stack

public void push(int x) {

q2.add(x);

top = x;

while (!q1.isEmpty()) {

q2.add(q1.remove());

}

Queue temp = q1;

q1 = q2;

q2 = temp;

}

复杂度分析

时间复杂度:O(n)

算法会让 q1 出队 nn 个元素,同时入队 n + 1 个元素到 q2。这个过程会产生 2n + 1步操作,同时链表中 插入 操作和 移除 操作的时间复杂度为 O(1),因此时间复杂度为 O(n)O(n)。

空间复杂度:O(1)

弹出(pop)

直接让 q1 中元素出队,同时将出队后的 q1 中的队首元素作为栈顶元素保存。

9509425725f6753fcfbc24dc75357a76.png

Figure 4. Pop an element from stack

// Removes the element on top of the stack.

public int pop() {

q1.remove();

int res = top;

if (!q1.isEmpty()) {

top = q1.peek();

}

return res;

}

复杂度分析

时间复杂度:O(1)O(1)

空间复杂度:O(1)O(1)

判断空(empty)和 取栈顶元素(top)是同样的实现方式。

判断空(empty)

q1 里包含了栈中所有的元素,所以只需要检查 q1 是否为空就可以了。

Java

// Returns whether the stack is empty.

public boolean empty() {

return q1.isEmpty();

}

时间复杂度:O(1)

空间复杂度:O(1)

取栈顶元素(top)

栈顶元素被保存在 top 变量里,每次我们 压入 或者 弹出 一个元素的时候都会随之更新这个变量。

Java

// Get the top element.

public int top() {

return top;

}

时间复杂度:O(1)

栈顶元素每次都是被提前计算出来的,同时只有 top 操作可以得到它的值。

空间复杂度:O(1)

方法三 (一个队列, 压入 - O(n), 弹出 - O(1))

上面介绍的两个方法都有一个缺点,它们都用到了两个队列。下面介绍的方法只需要使用一个队列。

算法

压入(push)

当我们将一个元素从队列入队的时候,根据队列的性质这个元素会存在队列的后端。

但当我们实现一个栈的时候,最后入队的元素应该在前端,而不是在后端。为了实现这个目的,每当入队一个新元素的时候,我们可以把队列的顺序反转过来。

3c86f2ed5f20391515e7519522fbef92.png

Figure 5. Push an element in stack

Java

private LinkedList q1 = new LinkedList<>();

// Push element x onto stack.

public void push(int x) {

q1.add(x);

int sz = q1.size();

while (sz > 1) {

q1.add(q1.remove());

sz--;

}

}

复杂度分析

时间复杂度:O(n)O(n)

这个算法需要从 q1 中出队 n 个元素,同时还需要入队 n个元素到 q1,其中 n是栈的大小。这个过程总共产生了 2n + 1 步操作。链表中 插入 操作和 移除 操作的时间复杂度为 O(1),因此时间复杂度为 O(n)。

空间复杂度:O(1)

弹出(pop)

最后一个压入的元素永远在 q1 的前端,这样的话我们就能在常数时间内让它 出队。

Java

// Removes the element on top of the stack.

public void pop() {

q1.remove();

}

复杂度分析

时间复杂度:O(1)

空间复杂度:O(1)

判断空(empty)

q1 中包含了栈中所有的元素,所以只需要检查 q1 是否为空就可以了。

Java

// Return whether the stack is empty.

public boolean empty() {

return q1.isEmpty();

}

时间复杂度:O(1)

空间复杂度:O(1)

取栈顶(top)

栈顶元素永远在 q1 的前端,直接返回就可以了。

Java

// Get the top element.

public int top() {

return q1.peek();

}

时间复杂度:O(1)

空间复杂度:O(1)

一个队列的完整实现:

class MyStack {

Queue q;

int top_elem;

/** Initialize your data structure here. */

public MyStack() {

q = new LinkedList<>();

top_elem=0;

}

/** Push element x onto stack. */

public void push(int x) {

q.offer(x);

top_elem = x;

}

/** Removes the element on top of the stack and returns that element. */

public int pop() {

int size = q.size();

while(size>2){

q.offer(q.poll());

size--;

}

top_elem = q.peek();

q.offer(q.poll());

return q.poll();

}

/** Get the top element. */

public int top() {

return top_elem;

}

/** Returns whether the stack is empty. */

public boolean empty() {

return q.isEmpty();

}

}

/**

* Your MyStack object will be instantiated and called as such:

* MyStack obj = new MyStack();

* obj.push(x);

* int param_2 = obj.pop();

* int param_3 = obj.top();

* boolean param_4 = obj.empty();

*/

另一种方法:

import java.util.LinkedList;

import java.util.Queue;

public class MyStack {

// 使用一个队列实现栈

private Queue queue;

/**

* Initialize your data structure here.

*/

public MyStack() {

queue = new LinkedList<>();

}

/**

* Push element x onto stack.

*/

public void push(int x) {

queue.offer(x);

}

private void shift() {

int size = queue.size();

for (int i = 0; i < size - 1; i++) {

queue.offer(queue.poll());

}

}

/**

* Removes the element on top of the stack and returns that element.

*/

public int pop() {

shift();

return queue.poll();

}

/**

* Get the top element.

*/

public int top() {

shift();

// 为了不打乱原来的 push 顺序,把这个元素再放到队尾

// 如果队列长度很长,连着几次 top,会很耗时

int res = queue.poll();

queue.offer(res);

return res;

}

/**

* Returns whether the stack is empty.

*/

public boolean empty() {

return queue.isEmpty();

}

}

/**

* Your MyStack object will be instantiated and called as such:

* MyStack obj = new MyStack();

* obj.push(x);

* int param_2 = obj.pop();

* int param_3 = obj.top();

* boolean param_4 = obj.empty();

*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值