题目描述

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。

实现 MyStack 类:

void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

注意:

你只能使用队列的标准操作 —— 也就是 push to back、peek/pop from front、size 和 is empty 这些操作。
你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。

示例:

输入:
[“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 次 push、pop、top 和 empty
  • 每次调用 pop 和 top 都保证栈不为空

进阶:你能否仅用一个队列来实现栈。

解题思路

栈的特性是后进先出(LIFO),而队列的特性是先进先出(FIFO)。

最开始的思路,就是用队列来存储数据,然后每次 pop 的时候,将队列中的元素全部出队,最后一个出队的元素就是栈顶元素。同时出队的元素重新入队到辅助队列中。这样就能实现栈的特性。

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
42
43
44
type MyStack struct {
queue []int
}

func Constructor() MyStack {
return MyStack{queue: []int{}}
}

func (this *MyStack) Push(x int) {
this.queue = append(this.queue, x)
}

func (this *MyStack) Pop() int {
ans := -1
newQueue := make([]int, 0)
for i := len(this.queue); i > 0; i-- {
ans = this.queue[0]
this.queue = this.queue[1:] // pop from queue

if i > 1 {
newQueue = append(newQueue, ans) // push into new queue
}
}
this.queue = newQueue // update the queue

return ans
}

func (this *MyStack) Top() int {
ans := -1
newQueue := make([]int, 0)
for i := len(this.queue); i > 0; i-- {
ans = this.queue[0]
this.queue = this.queue[1:] // pop from queue
newQueue = append(newQueue, ans) // push into new queue
}
this.queue = newQueue // update the queue

return ans
}

func (this *MyStack) Empty() bool {
return len(this.queue) == 0
}

上面的代码使用双队列来实现栈的功能,每次 push 就正常地将元素入队,而每次 poptop 都需要遍历整个队列,将元素出队并重新入队,时间复杂度为 O(n)。其中 pop 的操作中,最后一个元素出队后不再压入新队列中。

基于上面的思路,用一个队列也能完成。上面使用的双队列是因为每次出队后要把元素重新入对,最后将新队列赋值给原队列。实际上,我们可以在 push 的时候就将新元素放到队列的前面,这样在 poptop 的时候就不需要再申请一块空间用于存放原队列的元素了。

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
type MyStack struct {
queue []int
}

func Constructor() MyStack {
return MyStack{queue: make([]int, 0, 100)}
}

func (this *MyStack) Push(x int) {
this.queue = append(this.queue, x) // push into queue
}

func (this *MyStack) Pop() int {
ans := -1

for i, n := 0, len(this.queue); i < n; i++ {
ans = this.queue[0]
this.queue = this.queue[1:] // pop from queue

if i < n-1 {
this.queue = append(this.queue, ans) // push into queue
}
}

return ans
}

func (this *MyStack) Top() int {
ans := -1
for i := 0; i < len(this.queue); i++ {
ans = this.queue[0]
this.queue = append(this.queue[1:], ans) // pop and push into queue
}

return ans
}

func (this *MyStack) Empty() bool {
return len(this.queue) == 0
}

代码中需要注意的是,从队列中出队的次数是基于开始时队列的元素个数,而随着每次出队操作后,队列的元素个数会减少,所以在 poptop 的时候需要使用一个变量来记录开始时队列的长度。