[Leetcode][225. 用队列实现栈] 如何改变队列元素顺序?
By Long Luo
Leetcode 225. 用队列实现栈 ,难度为Easy:
- 用队列实现栈
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(\(\texttt{push}\)、\(\texttt{top}\)、\(\texttt{pop}\) 和 \(\texttt{empty}\))。
实现 \(\texttt{MyStack}\) 类:
- \(\texttt{void push(int x)}\) 将元素 \(\textit{x}\) 压入栈顶。
- \(\texttt{int pop()}\) 移除并返回栈顶元素。
- \(\texttt{int top()}\) 返回栈顶元素。
- \(\texttt{boolean empty()}\) 如果栈是空的,返回 \(\textit{true}\);否则,返回 \(\textit{false}\)。
注意: - 你只能使用队列的基本操作 —— 也就是push to back
、peek/pop from front
、size
和is empty
这些操作。 - 你所使用的语言也许不支持队列。 你可以使用list
(列表)或者deque
(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
示例:1
2
3
4
5
6
7
8
9
10
11
12
13输入:
["MyStack", "push", "push", "top", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]
解释:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False
提示: - 1 <= x <= 9 - 最多调用 \(100\) 次 \(\texttt{push}\)、\(\texttt{pop}\)、\(\texttt{top}\) 和 \(\texttt{empty}\) - 每次调用 \(\texttt{pop}\) 和 \(\texttt{top}\) 都保证栈不为空
进阶:你能否仅用一个队列来实现栈。
这道题考察的是栈和队列两种数据结构知识。
栈是一种后进先出(LIFO)的数据结构,元素从顶端入栈,然后从顶端出栈。
队列是一种先进先出(FIFO)的数据结构,元素从后端入队,然后从前端出队。
2个队列
题目的要求是用 \(2\) 个队列实现一个栈,但是把一个队列中的数据导入另一个队列中,数据的顺序并没有发生改变,并不会变成先进后出的顺序。
那该如何做呢?
用 \(2\) 个队列 \(\textit{queue}_1\) 和 \(\textit{queue}_2\):
- 有 \(1\) 个元素 \(\textit{A}\),那非常简单,直接压入弹出即可;
- 有 \(2\) 个元素 \(\textit{A}\),\(\textit{B}\),\(\textit{queue}_1\) 压入 \(A\),\(\textit{queue}_2\) 压入 \(B\),然后将 \(\textit{queue}_1\) 中元素压入 \(\textit{queue}_2\),这样 \(\textit{queue}_2\) 中的元素顺序就是正确的;
- 因为 \(\textit{queue}_1\) 是主队列,所以还需要将 \(\textit{queue}_2\) 中元素重新导入到 \(\textit{queue}_1\)。
代码如下所示:
1 | class MyStack { |
上述代码可以进行优化,\(\texttt{push}\) 这里实际上只需要交换 \(\textit{queue}_1\) 和 \(\textit{queue}_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
33class MyStack {
Queue<Integer> queue1;
Queue<Integer> queue2;
public MyStack() {
queue1 = new LinkedList<>();
queue2 = new LinkedList<>();
}
public void push(int x) {
queue2.offer(x);
while (!queue1.isEmpty()) {
queue2.add(queue1.poll());
}
Queue<Integer> temp = queue2;
queue2 = queue1;
queue1 = temp;
}
public int pop() {
return queue1.poll();
}
public int top() {
return queue1.peek();
}
public boolean empty() {
return queue1.isEmpty();
}
}
复杂度分析
- 时间复杂度:入栈操作 \(O(n)\),其余操作都是 \(O(1)\),其中 \(n\) 是栈内的元素个数。
- \(\texttt{push()}\) 入栈操作需要将 \(\textit{queue}_1\) 中的 \(n\) 个元素出队,并入队 \(n+1\) 个元素到 \(\textit{queue}_2\),共有 \(2n+1\) 次操作,每次出队和入队操作的时间复杂度都是 \(O(1)\),因此入栈操作的时间复杂度是 \(O(n)\)。
- \(\texttt{pop()}\) 出栈操作对应将 \(\textit{queue}_1\) 的前端元素出队,时间复杂度是 \(O(1)\)。
- \(\texttt{top()}\) 获得栈顶元素操作对应获得 \(\textit{queue}_1\) 的前端元素,时间复杂度是 \(O(1)\)。
- \(\texttt{empty()}\) 判断栈是否为空操作只需要判断 \(\textit{queue}_1\) 是否为空,时间复杂度是 \(O(1)\)。
- 空间复杂度:\(O(n)\),其中 \(n\) 是栈内的元素个数。需要使用两个队列存储栈内的元素。
1个队列
进阶要求是用一个队列去模拟栈,因为栈要求是最后入栈的元素最先出栈,所以队列前端的元素是最后入栈的元素。
入栈时,将元素入队到队列,再将队列头部的元素(除了最后一个元素外)依次出队并入队到队列,此时队列的前端的元素即为新入栈的元素,此时在去弹出元素就是栈的顺序了,队列的前端和后端分别对应栈顶和栈底。
代码如下所示: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
27class MyStack {
Queue<Integer> queue;
public MyStack() {
queue = new LinkedList<>();
}
public void push(int x) {
int len = queue.size();
queue.offer(x);
for (int i = 0; i < len; i++) {
queue.offer(queue.poll());
}
}
public int pop() {
return queue.poll();
}
public int top() {
return queue.peek();
}
public boolean empty() {
return queue.isEmpty();
}
}
复杂度分析
- 时间复杂度:入栈操作 \(O(n)\),其余操作都是 \(O(1)\),其中 \(n\) 是栈内的元素个数。
- \(\texttt{push()}\) 入栈操作需要将队列中的 \(n\) 个元素出队,并入队 \(n+1\) 个元素到队列,共有 \(2n+1\) 次操作,每次出队和入队操作的时间复杂度都是 \(O(1)\),因此入栈操作的时间复杂度是 \(O(n)\)。
- \(\texttt{pop()}\) 出栈操作对应将队列的前端元素出队,时间复杂度是 \(O(1)\)。
- \(\texttt{top()}\) 获得栈顶元素操作对应获得队列的前端元素,时间复杂度是 \(O(1)\)。
- \(\texttt{empty()}\) 判断栈是否为空操作只需要判断队列是否为空,时间复杂度是 \(O(1)\)。
- 空间复杂度:\(O(n)\),其中 \(n\) 是栈内的元素个数。需要使用两个队列存储栈内的元素。
All suggestions are welcome. If you have any query or suggestion please comment below. Please upvote👍 if you like💗 it. Thank you:-)
Explore More Leetcode Solutions. 😉😃💗