queue
stacks
data structures
implementation
algorithm

How can I implement a queue using two stacks?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

In data structures, a queue is an abstract data type that follows the First-In-First-Out (FIFO) principle. This means that the first element added to the queue will be the first one to be removed. On the other hand, a stack follows the Last-In-First-Out (LIFO) principle, where the last element added is the first to be removed.

Despite these differing behaviors, it's possible to implement a queue using two stacks. This can be particularly useful when working within constraints that require using stacks alone or when exploring different algorithm design patterns. In this article, we will explore how to implement a queue using two stacks.

Methodology

Components

To implement a queue using two stacks, you will need:

  • Two stacks: We'll call them Stack1 and Stack2.

Operations

For a queue, the essential operations are enqueue (adding an element) and dequeue (removing an element). Let's delve into implementing these operations using our two stacks.

Enqueue Operation

The enqueue operation is straightforward: simply push the element onto Stack1.

Pseudocode:

 
function enqueue(queue, element):
    queue.Stack1.push(element)

Dequeue Operation

The dequeue operation is where the logic becomes a bit more complex:

  1. If Stack2 is empty, pop all elements from Stack1 and push them onto Stack2.
  2. Pop the top element of Stack2, which is the FIFO element for the queue.

This inversion of elements from Stack1 to Stack2 ensures that the first element added to the queue is at the top of Stack2.

Pseudocode:

 
1function dequeue(queue):
2    if queue.Stack2.isEmpty():
3        while not queue.Stack1.isEmpty():
4            queue.Stack2.push(queue.Stack1.pop())
5    return queue.Stack2.pop()

Advantages and Trade-offs

Advantages

  • Maintains FIFO Order: Even while using stacks (inherently LIFO), the queue maintains the FIFO ordering through element transfer.

Trade-offs

  • Space Complexity: Two stacks are required, potentially doubling space usage.
  • Time Complexity: In the worst case, a single dequeue operation can be more time-consuming since it involves transferring all elements from Stack1 to Stack2.

Time Complexity Analysis

OperationAverage Time ComplexityWorst-case Time Complexity
EnqueueO(1)O(1)O(1)O(1)
DequeueO(1)O(1) (amortized)O(n)O(n)

In the context of amortized time complexity, while a single dequeue can take up to O(n)O(n) time in the worst scenario (when all elements are shifted from Stack1 to Stack2), over a sequence of operations, the average time is ``$O(1)$`.

Example Implementation

Let's have a look at a simple implementation in Python:

python
1class QueueUsingTwoStacks:
2    def __init__(self):
3        self.stack1 = []
4        self.stack2 = []
5
6    def enqueue(self, item):
7        self.stack1.append(item)
8
9    def dequeue(self):
10        if not self.stack2:
11            while self.stack1:
12                self.stack2.append(self.stack1.pop())
13        if self.stack2:
14            return self.stack2.pop()
15        else:
16            raise IndexError("Dequeue from an empty queue")
17
18# Example Usage
19queue = QueueUsingTwoStacks()
20queue.enqueue(1)
21queue.enqueue(2)
22queue.enqueue(3)
23print(queue.dequeue())  # Outputs: 1
24print(queue.dequeue())  # Outputs: 2
25queue.enqueue(4)
26print(queue.dequeue())  # Outputs: 3

Conclusion

This implementation leverages the characteristics of stacks to invert the order of insertion, effectively simulating the behavior of a queue. While it offers a clever demonstration of data structure manipulation, it's important to consider the trade-offs and understand the underlying mechanics thoroughly before employing this approach in production systems.


Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.