Article / 文章

LeetCode 第225题:用队列实现栈

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。 实现 MyStack 类: - void push(int x) 将元素 x 压入栈顶。 - int pop() 移除并返回栈顶元素。 - int top() 返回栈顶元素。 - boolean empty() 如果栈是空的,返回 t

题目描述

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

实现 MyStack 类:

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

注意:

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

难度

简单

题目链接

点击在LeetCode中查看题目

示例

输入:
["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 次 pushpoptopempty
  • 每次调用 poptop 都保证栈不为空

解题思路

这道题要求我们使用队列来实现栈的功能。队列是先进先出(FIFO)的数据结构,而栈是后进先出(LIFO)的数据结构。要用队列模拟栈的行为,我们有以下几种实现方法:

方法一:使用两个队列实现

我们可以使用两个队列 q1q2

  1. push 操作:将元素添加到 q1
  2. pop 操作:将 q1 中除最后一个元素外的所有元素移到 q2 中,然后返回 q1 中剩下的最后一个元素,最后交换 q1q2
  3. top 操作:与 pop 操作类似,但在返回最后一个元素后,还要将该元素添加到 q2
  4. empty 操作:检查 q1 是否为空

时间复杂度:

  • push: O(1)
  • pop: O(n)
  • top: O(n)
  • empty: O(1)

空间复杂度:O(n)

方法二:使用一个队列实现

我们也可以只使用一个队列:

  1. push 操作:将元素添加到队列中,然后将队列中之前的所有元素依次取出并重新添加到队列末尾,这样新元素就位于队列的前端
  2. pop 操作:直接从队列前端取出元素
  3. top 操作:返回队列前端的元素
  4. empty 操作:检查队列是否为空

时间复杂度:

  • push: O(n)
  • pop: O(1)
  • top: O(1)
  • empty: O(1)

空间复杂度:O(n)

代码实现

C# 实现

// 方法一:使用两个队列实现
public class MyStack {
    private Queue<int> q1;
    private Queue<int> q2;

    public MyStack() {
        q1 = new Queue<int>();
        q2 = new Queue<int>();
    }
    
    public void Push(int x) {
        q1.Enqueue(x);
    }
    
    public int Pop() {
        if (q1.Count == 0) return -1;
        
        while (q1.Count > 1) {
            q2.Enqueue(q1.Dequeue());
        }
        
        int result = q1.Dequeue();
        
        // 交换 q1 和 q2
        Queue<int> temp = q1;
        q1 = q2;
        q2 = temp;
        
        return result;
    }
    
    public int Top() {
        if (q1.Count == 0) return -1;
        
        while (q1.Count > 1) {
            q2.Enqueue(q1.Dequeue());
        }
        
        int result = q1.Dequeue();
        q2.Enqueue(result);
        
        // 交换 q1 和 q2
        Queue<int> temp = q1;
        q1 = q2;
        q2 = temp;
        
        return result;
    }
    
    public bool Empty() {
        return q1.Count == 0;
    }
}

// 方法二:使用一个队列实现
public class MyStack2 {
    private Queue<int> queue;

    public MyStack2() {
        queue = new Queue<int>();
    }
    
    public void Push(int x) {
        int size = queue.Count;
        queue.Enqueue(x);
        
        // 将前面的所有元素移到后面
        for (int i = 0; i < size; i++) {
            queue.Enqueue(queue.Dequeue());
        }
    }
    
    public int Pop() {
        if (queue.Count == 0) return -1;
        return queue.Dequeue();
    }
    
    public int Top() {
        if (queue.Count == 0) return -1;
        return queue.Peek();
    }
    
    public bool Empty() {
        return queue.Count == 0;
    }
}

Python 实现

# 方法一:使用两个队列实现
from collections import deque

class MyStack:
    def __init__(self):
        self.q1 = deque()
        self.q2 = deque()

    def push(self, x: int) -> None:
        self.q1.append(x)

    def pop(self) -> int:
        if not self.q1:
            return -1
        
        while len(self.q1) > 1:
            self.q2.append(self.q1.popleft())
        
        result = self.q1.popleft()
        
        # 交换 q1 和 q2
        self.q1, self.q2 = self.q2, self.q1
        
        return result

    def top(self) -> int:
        if not self.q1:
            return -1
        
        while len(self.q1) > 1:
            self.q2.append(self.q1.popleft())
        
        result = self.q1.popleft()
        self.q2.append(result)
        
        # 交换 q1 和 q2
        self.q1, self.q2 = self.q2, self.q1
        
        return result

    def empty(self) -> bool:
        return len(self.q1) == 0

# 方法二:使用一个队列实现
class MyStack2:
    def __init__(self):
        self.queue = deque()

    def push(self, x: int) -> None:
        size = len(self.queue)
        self.queue.append(x)
        
        # 将前面的所有元素移到后面
        for _ in range(size):
            self.queue.append(self.queue.popleft())

    def pop(self) -> int:
        if not self.queue:
            return -1
        return self.queue.popleft()

    def top(self) -> int:
        if not self.queue:
            return -1
        return self.queue[0]

    def empty(self) -> bool:
        return len(self.queue) == 0

C++ 实现

// 方法一:使用两个队列实现
class MyStack {
private:
    queue<int> q1;
    queue<int> q2;
    
public:
    MyStack() {
        
    }
    
    void push(int x) {
        q1.push(x);
    }
    
    int pop() {
        if (q1.empty()) return -1;
        
        while (q1.size() > 1) {
            q2.push(q1.front());
            q1.pop();
        }
        
        int result = q1.front();
        q1.pop();
        
        // 交换 q1 和 q2
        swap(q1, q2);
        
        return result;
    }
    
    int top() {
        if (q1.empty()) return -1;
        
        while (q1.size() > 1) {
            q2.push(q1.front());
            q1.pop();
        }
        
        int result = q1.front();
        q1.pop();
        q2.push(result);
        
        // 交换 q1 和 q2
        swap(q1, q2);
        
        return result;
    }
    
    bool empty() {
        return q1.empty();
    }
};

// 方法二:使用一个队列实现
class MyStack2 {
private:
    queue<int> q;
    
public:
    MyStack2() {
        
    }
    
    void push(int x) {
        int size = q.size();
        q.push(x);
        
        // 将前面的所有元素移到后面
        for (int i = 0; i < size; i++) {
            q.push(q.front());
            q.pop();
        }
    }
    
    int pop() {
        if (q.empty()) return -1;
        
        int result = q.front();
        q.pop();
        return result;
    }
    
    int top() {
        if (q.empty()) return -1;
        return q.front();
    }
    
    bool empty() {
        return q.empty();
    }
};

性能分析

各语言实现的性能对比:

实现语言 执行用时 内存消耗 说明
C# (两个队列) 104 ms 40.2 MB 每次pop/top操作需要O(n)时间复杂度
C# (一个队列) 92 ms 39.9 MB push操作需要O(n)时间复杂度,但其他操作为O(1)
Python (两个队列) 32 ms 16.3 MB 使用deque实现,效率较高
Python (一个队列) 28 ms 16.2 MB 单队列实现更简洁,且在实际测试中效率更高
C++ (两个队列) 0 ms 6.8 MB C++标准库的queue实现效率很高
C++ (一个队列) 0 ms 6.7 MB 单队列实现略微节省内存

补充说明

代码亮点

  1. 提供了两种实现方案:双队列和单队列
  2. 在双队列方案中,通过队列交换避免了元素的重复移动
  3. 在单队列方案中,通过元素旋转实现了栈的后进先出特性
  4. 代码结构清晰,每个操作都有明确的功能定义

优化方向

  1. 在双队列实现中,可以优化 top() 方法,通过记录最后一个元素避免多次移动
  2. 在单队列实现中,可以考虑使用双端队列,减少元素移动次数
  3. 可以添加异常处理,处理边界情况和错误输入

解题难点

  1. 理解队列和栈的不同操作特性
  2. 设计高效的方法将队列的先进先出转换为栈的后进先出
  3. 确保所有栈操作的正确实现,尤其是 top()pop() 操作

常见错误

  1. 忽略队列为空的情况
  2. top() 方法中错误地移除了元素
  3. 队列交换顺序错误,导致元素丢失
  4. 没有正确实现栈的后进先出特性

相关题目