Skip to content

一、问题描述

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

  • push(x) -- 将一个元素放入队列的尾部。
  • pop() -- 从队列首部移除元素。
  • peek() -- 返回队列首部的元素。
  • empty() -- 返回队列是否为空。

二、方案一:使用两个栈

1、思路

我们可以使用两个栈来实现队列。一个栈用于插入元素,另一个栈用于删除元素。当我们需要删除元素时,如果第二个栈为空,则将第一个栈的所有元素移动到第二个栈中,这样第二个栈的顶部就是队列的头部。

栈先入后出的特性导致不能在 push 时使用辅助,因为倒腾一次数据,并不能保持数据的单调性,所以在 pop 时使用

2、代码实现

go
type MyQueue struct {
    inStack, outStack []int
}
func Constructor() MyQueue {
    return MyQueue{}
}
func (this *MyQueue) Push(x int) {
    this.inStack = append(this.inStack, x)
}
func (this *MyQueue) Pop() int {
    this.peek()
    top := this.outStack[len(this.outStack)-1]
    this.outStack = this.outStack[:len(this.outStack)-1]
    return top
}
func (this *MyQueue) Peek() int {
    if len(this.outStack) == 0 {
        for len(this.inStack) > 0 {
            this.outStack = append(this.outStack, this.inStack[len(this.inStack)-1])
            this.inStack = this.inStack[:len(this.inStack)-1]
        }
    }
    return this.outStack[len(this.outStack)-1]
}
func (this *MyQueue) Empty() bool {
    return len(this.inStack) == 0 && len(this.outStack) == 0
}

3、复杂度分析

  • 时间复杂度:PushPop 在最坏情况下是 O(n),但均摊下来是 O(1)。
  • 空间复杂度:O(n),因为我们需要额外的空间来存储两个栈中的元素。

三、方案二:使用一个栈

1、思路

我们也可以只使用一个栈来实现队列。每次插入元素时,如果栈不为空,我们需要将栈中的所有元素弹出并存储在另一个临时栈中,然后将新元素插入栈底,最后将临时栈中的元素再放回原栈。

2次倒腾,栈中的数据保持先入后出

2、代码实现

go
type MyQueue struct {
    stack []int
}
func Constructor() MyQueue {
    return MyQueue{}
}
func (this *MyQueue) Push(x int) {
    tempStack := []int{}
    for len(this.stack) > 0 {
        tempStack = append(tempStack, this.stack[len(this.stack)-1])
        this.stack = this.stack[:len(this.stack)-1]
    }
    this.stack = append(this.stack, x)
    for len(tempStack) > 0 {
        this.stack = append(this.stack, tempStack[len(tempStack)-1])
        tempStack = tempStack[:len(tempStack)-1]
    }
}
func (this *MyQueue) Pop() int {
    top := this.stack[len(this.stack)-1]
    this.stack = this.stack[:len(this.stack)-1]
    return top
}
func (this *MyQueue) Peek() int {
    return this.stack[len(this.stack)-1]
}
func (this *MyQueue) Empty() bool {
    return len(this.stack) == 0
}

3、复杂度分析

  • 时间复杂度:Push 是 O(n),PopPeek 是 O(1)。
  • 空间复杂度:O(n),因为我们需要额外的空间来存储临时栈中的元素。

四、总结

方案时间复杂度空间复杂度备注
使用两个栈均摊 O(1)O(n)更优的均摊时间复杂度
使用一个栈O(n)O(n)简单但时间复杂度较高

推荐使用两个栈的方案,因为它有更优的均摊时间复杂度。

木川工作室 (微信:mcmc2024)