Algorithms 101: How to implement a queue by using two stacks


用栈实现队列

In this problem, we are required to implement a queue using two stacks. The queue should support all four standard operations: push, pop, peek, and empty.

在这个问题中,我们需要使用两个栈来实现一个队列。该队列应支持所有四种标准操作:pushpoppeekempty

Approach

The key idea to implement a queue using two stacks is to use one stack (stack1) to handle incoming elements (push operations) and another stack (stack2) to handle outgoing elements (pop and peek operations). The approach works as follows:

使用两个栈实现队列的关键思路是使用一个栈(stack1)处理传入的元素(push 操作),另一个栈(stack2)处理传出的元素(pop 和 peek 操作)。该方法的工作原理如下:

  1. Push Operation:

    • Always push the new element into stack1.

    Push 操作

    • 始终将新元素推入 stack1
  2. Pop Operation:

    • If stack2 is empty, transfer all elements from stack1 to stack2. Then, pop the top element from stack2.

    Pop 操作

    • 如果 stack2 为空,将 stack1 中的所有元素转移到 stack2 中。然后,从 stack2 中弹出顶部元素。
  3. Peek Operation:

    • If stack2 is empty, transfer all elements from stack1 to stack2. Then, return the top element from stack2.

    Peek 操作

    • 如果 stack2 为空,将 stack1 中的所有元素转移到 stack2 中。然后,从 stack2 中返回顶部元素。
  4. Empty Operation:

    • The queue is empty if both stack1 and stack2 are empty.

    Empty 操作

    • 如果 stack1stack2 都为空,则队列为空。

Example Implementation

Here is how the MyQueue class can be implemented:

下面是 MyQueue 类的实现:

class MyQueue:

    def __init__(self):
        self.stack1 = []
        self.stack2 = []

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

    def pop(self) -> int:
        self.peek()
        return self.stack2.pop()

    def peek(self) -> int:
        if not self.stack2:
            while self.stack1:
                self.stack2.append(self.stack1.pop())
        return self.stack2[-1]

    def empty(self) -> bool:
        return not self.stack1 and not self.stack2

Explanation

  • Initialization: Two stacks are initialized: stack1 and stack2.

    初始化:初始化了两个栈:stack1stack2

  • Push Operation: The push method appends the element to stack1.

    Push 操作push 方法将元素追加到 stack1 中。

  • Pop Operation: The pop method ensures that stack2 is populated by transferring elements from stack1, then pops the top element from stack2.

    Pop 操作pop 方法确保 stack2 被填充,通过将元素从 stack1 转移到 stack2,然后从 stack2 弹出顶部元素。

  • Peek Operation: The peek method checks if stack2 is empty and, if so, transfers elements from stack1 to stack2, then returns the top element of stack2.

    Peek 操作peek 方法检查 stack2 是否为空,如果为空,则将元素从 stack1 转移到 stack2,然后返回 stack2 的顶部元素。

  • Empty Operation: The empty method checks if both stacks are empty.

    Empty 操作empty 方法检查两个栈是否都为空。

Time Complexity

  • Push Operation: O(1)
  • Pop Operation: Amortized O(1)
  • Peek Operation: Amortized O(1)
  • Empty Operation: O(1)

时间复杂度

  • Push 操作:O(1)
  • Pop 操作:均摊 O(1)
  • Peek 操作:均摊 O(1)
  • Empty 操作:O(1)

Interview Questions

  1. Why do we need two stacks to implement a queue?

    • Two stacks are required to reverse the order of elements so that we can achieve FIFO behavior using LIFO stacks.

    为什么需要两个栈来实现队列?

    • 需要两个栈来反转元素的顺序,以便我们可以使用 LIFO 栈实现 FIFO 行为。
  2. What is the time complexity of the pop operation?

    • The time complexity of the pop operation is amortized O(1) because each element is transferred between stacks at most once.

    Pop 操作的时间复杂度是多少?

    • Pop 操作的时间复杂度是均摊 O(1),因为每个元素在栈之间的转移最多只发生一次。
  3. Can you optimize the space complexity of this implementation?

    • The space complexity is already optimal, as it uses O(n) space, where n is the number of elements in the queue.

    你能优化此实现的空间复杂度吗?

    • 空间复杂度已经是最优的,因为它使用 O(n) 空间,其中 n 是队列中的元素数量。
  4. How does this approach differ from using a single stack?

    • Implementing a queue with a single stack is not straightforward because it requires additional logic to maintain the order of elements for FIFO behavior.

    这种方法与使用单个栈有何不同?

    • 使用单个栈实现队列并不简单,因为它需要额外的逻辑来维护元素的顺序以实现 FIFO 行为。
  5. What are the trade-offs of using this method to implement a queue?

    • The trade-off is that while push is efficient, pop and peek operations require transferring elements between stacks, leading to amortized O(1) complexity.

    使用这种方法实现队列的权衡是什么?

    • 权衡在于虽然 push 操作是高效的,但 pop 和 peek 操作需要在栈之间转移元素,导致均摊 O(1) 的复杂度。

Conclusion

Implementing a queue using two stacks is a practical and efficient way to achieve FIFO behavior with LIFO structures. It ensures that operations remain efficient while maintaining the order of elements.

结论

使用两个栈实现队列是一种实用且高效的方法,可以通过 LIFO 结构实现 FIFO 行为。它确保在维护元素顺序的同时保持操作的高效性。


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *