总的来说有两种方法:
- 用
两个
队列实现- 用
一个
队列实现
方法一:用两个
队列实现
最好的方法就是让两个队列q1
(入栈队列)和q2
(出栈队列)分别充当入栈和出栈的角色,即:
push
操作内部将元素添加到q1
pop
从q2
中poll
元素
q1
和q2
只是一个指针,在 push
内部利用一个临时变量来时刻交换q1
和q2
的指向,使得q1
和q2
分别指向的队列始终分别指向 入栈队列
和 出栈队列
。
这样做的好处是,可以简化pop()
与top()
的实现,因为s2
是出栈队列,所以只需要分别执行 q2.poll()
和 q2.peek()
即可。
主要是push()
的实现比较复杂,下面来通过几张图来阐述添加元素的过程:
Java实现如下:
class MyStack {
private Queue<Integer> q1;
private Queue<Integer> q2;
public MyStack() {
q1 = new LinkedList<Integer>();
q2 = new LinkedList<Integer>();
}
// 只要是进栈操作就添加到q1,只要是出栈操作就从q2中poll
public void push(int x) {
q1.offer(x);
while(!q2.isEmpty()){
q1.offer(q2.poll());
}
// 通过一个临时的队列来交换q1和q2的指向
Queue<Integer> temp; // 这里只是声明一个变量,并没有开辟一个真正的队列空间
temp = q1;
q1 = q2;
q2 = temp;
}
public int pop() {
return q2.poll();
}
public int top() {
return q2.peek();
}
public boolean empty() {
return q1.isEmpty() && q2.isEmpty();
}
}
时间复杂度分析
- push()
入栈操作需要将 q2
中的 n
个元素出队,并入队 n+1
个元素到 q1
,共有 2n+1
次操作,每次出队和入队操作的时间复杂度都是 O(1)
,因此入栈操作的时间复杂度是 O(n)
。
- 某次的push的时间复杂度取决于出栈队列是否为空,若没有pop操作(或者pop的次数远小于push),这样,几乎每次的push操作的复杂度都为O(n),因为几乎每次q2都不为空。
- 其余都是
O(1)
方法二:用一个
队列实现
同样,用一个队列实现的时候,主要的实现在于push()
操作。
具体做法如下:
- 在每次添加一个元素之前,先将当前队列中的元素个数记下为
n
- 然后执行
queue.offer(x);
将元素添加到queue
- 接下来比较梦幻的一步就是,将
queue
中的前n
个元素poll
出依次offer
到queue
- 这样,目前处于队首的位置必定是刚刚新添加的
x
下面来通过几张图来阐述添加元素的过程:
Java 实现如下:
class MyStack {
Queue<Integer> queue;
public MyStack() {
queue = new LinkedList<Integer>();
}
public void push(int x) {
int n = queue.size();
queue.offer(x);
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();
}
}
时间复杂度分析
- push()
每一次的push
操作都会将n个元素重新添加到queue
中,因此push()
的时间复杂度为O(n)
- 其余都是
O(1)