Nameless Site

But one day, you will stand before its decrepit gate,without really knowing why.

0%

用栈实现队列

来自Leetcode第232题用栈实现队列

使用栈实现队列的下列操作:

  • push(x) — 将一个元素放入队列的尾部。
  • pop() — 从队列首部移除元素。
  • peek() — 返回队列首部的元素。
  • empty() — 返回队列是否为空。 示例:
1
2
3
4
5
6
7
MyQueue queue = new MyQueue();

queue.push(1);
queue.push(2);
queue.peek(); // 返回 1
queue.pop(); // 返回 1
queue.empty(); // 返回 false

说明:

  • 你只能使用标准的栈操作 — 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
  • 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。

铁憨憨

一开始想用1个栈实现队列,但是发现1个栈倒腾之后栈里元素的顺序会错乱,因而还是老老实实用了2个栈,一个用来压入元素,一个用来实现出队列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class MyQueue {

Stack<Integer> instack = new Stack<>();
Stack<Integer> outstack = new Stack<>();
/** Initialize your data structure here. */
public MyQueue() {

}

/** Push element x to the back of queue. */
public void push(int x) {
while (!outstack.empty())
instack.push(outstack.pop());
instack.push(x);
while (!instack.empty())
outstack.push(instack.pop());
}

/** Removes the element from in front of queue and returns that element. */
public int pop() {
return outstack.pop();
}

/** Get the front element. */
public int peek() {
return outstack.peek();
}

/** Returns whether the queue is empty. */
public boolean empty() {
return outstack.empty();
}
}

执行用时为 0 ms 的范例

仔细看了看,这个范例8行,还得看题解的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class MyQueue {
Stack<Integer> s1, s2;
int size = 0;
int top = 0;
/** Initialize your data structure here. */
public MyQueue() {
s1 = new Stack<>();
s2 = new Stack<>();
}

/** Push element x to the back of queue. */
public void push(int x) {
s1.push(x);
if (size == 0) top = x;
size ++;
}

/** Removes the element from in front of queue and returns that element. */
public int pop() {
for (int i = 0; i < size - 1; i ++) {
if (i == size - 2) {
top = s1.pop();
s2.push(top);
} else s2.push(s1.pop());
}
int element = s1.pop();
size --;
for (int i = 0; i < size; i ++) s1.push(s2.pop());
return element;
}

/** Get the front element. */
public int peek() {
return top;
}

/** Returns whether the queue is empty. */
public boolean empty() {
return size == 0;
}
}

使用两个栈 入队 - O(1),出队 - 摊还复杂度 O(1)

来自官方题解

新元素总是压入 s1 的栈顶,同时我们会把 s1 中压入的第一个元素赋值给作为队首元素的 front 变量。

1
2
3
4
5
6
7
8
9
private Stack<Integer> s1 = new Stack<>();
private Stack<Integer> s2 = new Stack<>();

// Push element x to the back of queue.
public void push(int x) {
if (s1.empty())
front = x;
s1.push(x);
}

根据栈 LIFO 的特性,s1 中第一个压入的元素在栈底。为了弹出 s1 的栈底元素,我们得把 s1 中所有的元素全部弹出,再把它们压入到另一个栈 s2 中,这个操作会让元素的入栈顺序反转过来。通过这样的方式,s1 中栈底元素就变成了 s2 的栈顶元素,这样就可以直接从 s2 将它弹出了。一旦 s2 变空了,我们只需把 s1 中的元素再一次转移到 s2 就可以了。

1
2
3
4
5
6
7
8
// Removes the element from in front of queue.
public void pop() {
if (s2.isEmpty()) {
while (!s1.isEmpty())
s2.push(s1.pop());
}
s2.pop();
}

s1s2 都存有队列的元素,所以只需要检查 s1s2 是否都为空就可以了。

1
2
3
4
// Return whether the queue is empty.
public boolean empty() {
return s1.isEmpty() && s2.isEmpty();
}

我们定义了 front 变量来保存队首元素,每次 入队 操作我们都会随之更新这个变量。当 s2 为空,front 变量就是对首元素,当 s2 非空,s2 的栈顶元素就是队首元素。

1
2
3
4
5
6
7
// Get the front element.
public int peek() {
if (!s2.isEmpty()) {
return s2.peek();
}
return front;
}