[232][简单][栈][队列] 用栈实现队列

题目描述

232. 用栈实现队列 剑指 Offer 09. 用两个栈实现队列

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列的支持的所有操作(push、pop、peek、empty):

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾

  • int pop() 从队列的开头移除并返回元素

  • int peek() 返回队列开头的元素

  • boolean empty() 如果队列为空,返回 true ;否则,返回 false

说明:

  • 你只能使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。

  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

进阶:

  • 你能否实现每个操作均摊时间复杂度为 O(1) 的队列?换句话说,执行 n 个操作的总时间复杂度为 O(n) ,即使其中一个操作可能花费较长时间。

示例:

输入:
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]

解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false

提示:

  • 1 <= x <= 9

  • 最多调用 100 次 push、pop、peek 和 empty

  • 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)

解题思路

维护两个栈, 将一个元素从一个栈弹出, 放到另一个栈中. 之前后进入栈的元素, 即栈顶的元素, 在新栈的栈底, 而先进栈的元素在新栈的栈顶. 因此对新栈弹出, 首先弹出的就是之前最先进栈的元素. 这样就成功模拟了队列先进先出的行为.

这种现象可以总结为两个栈的一次元素转移, 就逆转了一次数字的先后顺序. 如果还要在逆转过来, 再在两个栈之间转移一次就好.

我们创建两个栈, 分别记为s1和s2. s1作为接收栈, 新来的元素压入其中; s2作为弹出栈, pop操作由这个栈弹出. 按各种操作分析.

push

新元素直接压入s1的栈顶, 对应的时间复杂度是O(1)O(1).

pop

之前说过pop从s2中取数, 因为s2中的元素经过一次转移, 先后顺序已经经过一次逆转. 如果s2中有数, 直接弹出即可. 如果s2中没有数, 需要将s1中所有的数依次弹出压入到s2中. 必须是s1中所有的数, 否则会发生顺序的混乱.

最坏情况下的时间复杂度为O(n)O(n), 但衡量所有操作的平均性能对应的摊还复杂度O(1)O(1). 因此一次最坏的情况发生后, 说明之前和之后很长一段时间都不会发生.

直接考虑每个元素, 无论是哪个元素, 都是入栈两次(进一次A和一次B), 出栈两次, 因此平均后的时间复杂度为O(1)O(1).

peek(查询队首元素)

如果s2不为空, 返回栈顶元素的值. 如果s2为空, s1不为空, 就像pop方法一样, 将s1中所有元素弹出并压入s2中, 然后返回s2的栈顶元素.

empty

s1, s2都为空则队列为空.

class MyQueue:
    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.stack1 = []
        self.stack2 = []

    def push(self, x: int) -> None:
        """
        Push element x to the back of queue.
        """
        self.stack1.append(x)

    def pop(self) -> int:
        """
        Removes the element from in front of queue and returns that element.
        """
        if not self.stack2:
            self.move()
        return self.stack2.pop() if self.stack2 else None

    def peek(self) -> int:
        """
        Get the front element.
        """
        if not self.stack2:
            self.move()
        return self.stack2[-1] if self.stack2 else None

    def empty(self) -> bool:
        """
        Returns whether the queue is empty.
        """
        return not self.stack1 and not self.stack2

    def move(self):
        while self.stack1:
            self.stack2.append(self.stack1.pop())

参考资料

最后更新于