Using Two Stacks To Implement A Queue

A queue is a fundamental data structure that follows the First-In-First-Out (FIFO) principle, meaning that elements are added at one end (rear) and removed from the other (front). Stacks, on the other hand, follow the Last-In-First-Out (LIFO) principle, where elements are both added and removed from the top.

Although queues and stacks operate differently, a queue can be efficiently implemented using two stacks. This approach is useful in situations where a queue is required, but only stacks are available.

This topic will explore how to implement a queue using two stacks, explain the push and pop operations, analyze time complexity, and discuss real-world applications.

1. Understanding the Concept

1.1 Why Use Two Stacks?

A stack only allows operations at one end, making direct queue implementation difficult. However, by combining two stacks, we can simulate a queue while maintaining FIFO order.

1.2 How It Works

We use two stacks:
Stack 1 (Input Stack): Used for inserting (enqueueing) elements.
Stack 2 (Output Stack): Used for removing (dequeueing) elements.

When an element is added, it is pushed onto Stack 1. When an element needs to be removed, it is transferred to Stack 2, ensuring that the first inserted element is removed first.

2. Implementing a Queue Using Two Stacks

There are two main approaches:

Method 1: Push Operation is Costly
Method 2: Pop Operation is Costly

2.1 Method 1: Making Enqueue Costly

In this method, every time we enqueue (push) a new element, we first move all elements from Stack 1 to Stack 2, insert the new element at the bottom, and then move everything back.

Algorithm:

  1. Move all elements from Stack 1 to Stack 2.

  2. Push the new element onto Stack 1.

  3. Move all elements back from Stack 2 to Stack 1.

  4. When dequeuing, simply pop from Stack 1.

Code Implementation (Python):

class QueueUsingTwoStacks:def __init__(self):self.stack1 = []self.stack2 = []def enqueue(self, item):while self.stack1:self.stack2.append(self.stack1.pop())self.stack1.append(item)while self.stack2:self.stack1.append(self.stack2.pop())def dequeue(self):if not self.stack1:return "Queue is empty"return self.stack1.pop()

Time Complexity:

Enqueue (Push) Operation: O(n)
Dequeue (Pop) Operation: O(1)

This method ensures quick dequeuing, but insertion is slow.

2.2 Method 2: Making Dequeue Costly

Instead of making push (enqueue) expensive, we can make pop (dequeue) expensive by only transferring elements when necessary.

Algorithm:

  1. Push elements into Stack 1 as usual.

  2. When dequeuing:

    • If Stack 2 is empty, move all elements from Stack 1 to Stack 2.

    • Pop the top element from Stack 2.

Code Implementation (Python):

class QueueUsingTwoStacks:def __init__(self):self.stack1 = []self.stack2 = []def enqueue(self, item):self.stack1.append(item)def dequeue(self):if not self.stack2:while self.stack1:self.stack2.append(self.stack1.pop())if not self.stack2:return "Queue is empty"return self.stack2.pop()

Time Complexity:

Enqueue (Push) Operation: O(1)
Dequeue (Pop) Operation: Amortized O(1)

This method ensures fast insertion and improves overall efficiency.

3. Comparison of Both Methods

Approach Enqueue Complexity Dequeue Complexity Overall Performance
Method 1 (Costly Enqueue) O(n) O(1) Slow insertion but fast removal
Method 2 (Costly Dequeue) O(1) Amortized O(1) More efficient for real-world applications

4. Time Complexity Analysis

4.1 Amortized Complexity

In Method 2, moving elements to Stack 2 only happens when Stack 2 is empty, meaning that each element is moved at most once before being dequeued. Thus, the amortized time complexity remains O(1) per operation.

4.2 Space Complexity

Both methods require two stacks, meaning the space complexity is O(n), where n is the number of elements in the queue.

5. Real-World Applications

5.1 Job Scheduling

Operating systems and task schedulers use queues for managing processes. Implementing queues with stacks can be useful in environments where stack-based memory allocation is preferred.

5.2 Undo-Redo Functionality

Software applications often use two stacks to track user actions:
✔ One stack stores actions (undo operations).
✔ The other stack stores undone actions (redo operations).

This behavior closely resembles the queue implementation using two stacks.

5.3 Web Browser Back-Forward Navigation

Web browsers maintain history using two stacks:
Back stack: Stores previously visited pages.
Forward stack: Stores pages revisited after using the back button.

This allows users to navigate seamlessly between past and future pages.

6. Advantages and Disadvantages

6.1 Advantages

Efficient Use of Memory – Uses two simple stacks without additional data structures.
Optimized Performance – The amortized complexity of Method 2 ensures fast queue operations.
Stack-Based Execution – Useful in stack-based memory management environments.

6.2 Disadvantages

Increased Complexity – More complex compared to a direct queue implementation.
Extra Space Usage – Requires two stacks instead of a single queue structure.

7. Alternative Approaches

Although using two stacks is a common way to implement a queue, other methods exist:

Using a Linked List – A queue can be implemented efficiently with a singly linked list.
Using a Circular Array – Circular queues reduce space wastage compared to a standard array.
Deque (Double-Ended Queue) – Supports both stack and queue operations in an optimized way.

Implementing a queue using two stacks is an interesting way to combine FIFO and LIFO principles. By carefully choosing between costly enqueue vs. costly dequeue, we can optimize performance based on specific use cases.

For general applications, the second method (costly dequeue) is preferred due to its O(1) amortized time complexity for both operations.

Understanding this concept is essential for coding interviews, data structure optimizations, and real-world software applications. Whether you’re building a scheduling system, an undo-redo mechanism, or a web navigation tool, knowing how to implement a queue using two stacks can be a valuable skill.