LeetCode 622: Design Circular Queue Solution in Python – A Step-by-Step Guide

Imagine you’re building a ride-sharing app where passengers line up for cars, but the queue loops around—when it’s full, the next passenger waits for the front spot to free up, not the end. That’s the idea behind LeetCode 622: Design Circular Queue, a medium-level problem that’s all about creating a data structure to manage a fixed-size, circular queue. Using Python, we’ll explore two solutions: the Best Solution, an array-based circular queue with pointers for efficiency, and an Alternative Solution, a linked list-based approach that’s more flexible but less space-efficient. With detailed examples, beginner-friendly breakdowns—especially for the array method—and clear code, this guide will help you design that queue, whether you’re new to coding or leveling up. Let’s set up our queue and start managing!

What Is LeetCode 622: Design Circular Queue?

In LeetCode 622: Design Circular Queue, you’re tasked with implementing a MyCircularQueue class with a fixed capacity k and these methods:

  • enQueue(value): Add an element if space exists.
  • deQueue(): Remove the front element if not empty.
  • Front(): Return the front element, or -1 if empty.
  • Rear(): Return the rear element, or -1 if empty.
  • isEmpty(): Check if queue is empty.
  • isFull(): Check if queue is full.

Unlike a regular queue, this one is circular—when the end is reached, it wraps around to the start. For example, with k=3, you can add 1, 2, 3, dequeue 1, and add 4 at the start. This problem tests your ability to design a data structure with wrap-around logic.

Problem Statement

  • Input:
    • k: Integer capacity (1 ≤ k ≤ 1000).
    • Method calls with values (0 ≤ value ≤ 1000).
  • Output: A class implementing the circular queue methods.
  • Rules:
    • Fixed size k.
    • Circular: Rear wraps to front when full and space opens.

Constraints

  • 1 ≤ k ≤ 1000.
  • 0 ≤ value ≤ 1000.
  • Up to 3000 method calls.

Examples

  • Input:
    • ["MyCircularQueue", "enQueue", "enQueue", "enQueue", "enQueue", "Rear", "isFull", "deQueue", "enQueue"]
    • [[3], [1], [2], [3], [4], [], [], [], [4]]
    • Output: [null, true, true, true, false, 3, true, true, true]
      • Init k=3, add 1,2,3 (full), 4 fails, rear=3, dequeue 1, add 4.
  • Input:
    • ["MyCircularQueue", "enQueue", "deQueue", "Front"]
    • [[1], [1], [], []]
    • Output: [null, true, true, -1]
      • Init k=1, add 1, remove 1, front empty.

These examples show the flow—let’s solve it!

Understanding the Problem: Designing a Circular Queue

To solve LeetCode 622: Design Circular Queue in Python, we need to create a class that manages a fixed-size queue where the rear loops back to the front when space is available. A naive approach might use a regular list and shift elements, but that’s slow. We’ll use:

  • Best Solution (Array-Based with Pointers): O(1) time per operation, O(k) space—fast and space-efficient.
  • Alternative Solution (Linked List-Based): O(1) time per operation, O(k) space—flexible but uses more memory.

Let’s start with the array-based solution, breaking it down for beginners.

Best Solution: Array-Based Circular Queue with Pointers

Why This Is the Best Solution

The array-based circular queue with pointers is the top choice for LeetCode 622 because it’s efficient—O(1) time for all operations—and uses a fixed array with two pointers (front and rear) to track the queue’s state, minimizing space to O(k). It’s the classic circular queue implementation, ideal for fixed-size constraints (k ≤ 1000). It’s like a ring of slots where you track the start and end!

How It Works

Think of this as a circular ticket line:

  • Step 1: Initialize:
    • Array of size k, front/rear pointers, size counter.
  • Step 2: Enqueue:
    • If not full, add at rear, increment rear (mod k), update size.
  • Step 3: Dequeue:
    • If not empty, move front (mod k), decrease size.
  • Step 4: Front/Rear:
    • Return values at pointers, or -1 if empty.
  • Step 5: Check Empty/Full:
    • Empty: size = 0.
    • Full: size = k.

It’s like a ring manager—track and loop!

Step-by-Step Example

Example: k=3, Operations: enQueue(1), enQueue(2), enQueue(3), deQueue(), enQueue(4)

  • Init: queue = [None, None, None], front = 0, rear = 0, size = 0.
  • enQueue(1):
    • Not full, queue[0] = 1, rear = 1, size = 1.
    • [1, None, None].
  • enQueue(2):
    • queue[1] = 2, rear = 2, size = 2.
    • [1, 2, None].
  • enQueue(3):
    • queue[2] = 3, rear = 0 (wrap), size = 3.
    • [1, 2, 3].
  • enQueue(4):
    • Full (size = k), return False.
  • Rear(): queue[2] = 3, return 3.
  • isFull(): size = 3 = k, return True.
  • deQueue():
    • Not empty, front = 1, size = 2, return True.
    • [1, 2, 3] (1 logically removed).
  • enQueue(4):
    • queue[0] = 4, rear = 1, size = 3.
    • [4, 2, 3].

Code with Detailed Line-by-Line Explanation

Here’s the Python code, explained clearly:

class MyCircularQueue:
    def __init__(self, k: int):
        # Step 1: Initialize array and pointers
        self.k = k
        self.queue = [None] * k
        self.front = 0  # Index of front element
        self.rear = 0   # Index where next element goes
        self.size = 0   # Current number of elements

    def enQueue(self, value: int) -> bool:
        # Step 2: Add element if not full
        if self.isFull():
            return False
        self.queue[self.rear] = value
        self.rear = (self.rear + 1) % self.k  # Wrap around
        self.size += 1
        return True

    def deQueue(self) -> bool:
        # Step 3: Remove element if not empty
        if self.isEmpty():
            return False
        self.front = (self.front + 1) % self.k  # Wrap around
        self.size -= 1
        return True

    def Front(self) -> int:
        # Step 4: Return front element
        if self.isEmpty():
            return -1
        return self.queue[self.front]

    def Rear(self) -> int:
        # Step 5: Return rear element
        if self.isEmpty():
            return -1
        return self.queue[(self.rear - 1) % self.k]

    def isEmpty(self) -> bool:
        # Step 6: Check if empty
        return self.size == 0

    def isFull(self) -> bool:
        # Step 7: Check if full
        return self.size == self.k
  • Lines 3-8: Init:
    • Array of size k, pointers at 0, size tracker.
  • Lines 11-16: enQueue:
    • Add at rear, wrap with modulo, increment size.
  • Lines 19-23: deQueue:
    • Move front, wrap, decrease size.
  • Lines 26-29: Front: Return queue[front] or -1.
  • Lines 32-35: Rear: Return queue[rear-1] or -1.
  • Lines 38-40: isEmpty: Size = 0.
  • Lines 43-45: isFull: Size = k.
  • Time Complexity: O(1) per operation.
  • Space Complexity: O(k)—fixed array.

This is like a ring queue—fast and tight!

Alternative Solution: Linked List-Based Circular Queue

Why an Alternative Approach?

The linked list-based approach uses nodes with pointers—O(1) time per operation, O(k) space. It’s more flexible, dynamically linking elements, but uses extra memory for pointers and isn’t as cache-friendly as an array. It’s like a chain of tickets looping around!

How It Works

Picture this as a linked chain:

  • Step 1: Init with head/tail pointers and size.
  • Step 2: Enqueue: Add node at tail, link to head if full.
  • Step 3: Dequeue: Move head, adjust tail if empty.
  • Step 4: Front/Rear: Access head/tail values.
  • Step 5: Empty/Full: Check size vs capacity.

It’s like a flexible queue loop!

Step-by-Step Example

Example: k=2, enQueue(1), enQueue(2), deQueue(), enQueue(3)

  • Init: head = None, tail = None, size = 0, k = 2.
  • enQueue(1):
    • head = tail = Node(1), size = 1.
  • enQueue(2):
    • tail.next = Node(2), tail = tail.next, size = 2.
    • tail.next = head (circular).
  • deQueue():
    • head = head.next, size = 1, return True.
    • [2], tail.next = head.
  • enQueue(3):
    • tail.next = Node(3), tail = tail.next, size = 2.
    • [2, 3], tail.next = head.

Code for Linked List Approach

class Node:
    def __init__(self, value):
        self.value = value
        self.next = None

class MyCircularQueue:
    def __init__(self, k: int):
        self.k = k
        self.head = None
        self.tail = None
        self.size = 0

    def enQueue(self, value: int) -> bool:
        if self.isFull():
            return False
        new_node = Node(value)
        if self.isEmpty():
            self.head = self.tail = new_node
        else:
            self.tail.next = new_node
            self.tail = new_node
        self.tail.next = self.head  # Circular link
        self.size += 1
        return True

    def deQueue(self) -> bool:
        if self.isEmpty():
            return False
        self.head = self.head.next
        self.tail.next = self.head
        self.size -= 1
        if self.isEmpty():
            self.head = self.tail = None
        return True

    def Front(self) -> int:
        return -1 if self.isEmpty() else self.head.value

    def Rear(self) -> int:
        return -1 if self.isEmpty() else self.tail.value

    def isEmpty(self) -> bool:
        return self.size == 0

    def isFull(self) -> bool:
        return self.size == self.k
  • Lines 7-12: Init with pointers.
  • Lines 14-22: enQueue: Add node, link circularly.
  • Lines 25-32: deQueue: Move head, adjust links.
  • Time Complexity: O(1) per operation.
  • Space Complexity: O(k)—nodes.

It’s a chain queue—flexible but heavier!

Comparing the Two Solutions

  • Array-Based (Best):
    • Pros: O(1) time, O(k) space, cache-friendly.
    • Cons: Fixed structure.
  • Linked List (Alternative):
    • Pros: O(1) time, O(k) space, dynamic.
    • Cons: More memory, less efficient.

Array-based wins for efficiency.

Additional Examples and Edge Cases

  • Input: k=1, enQueue(1), enQueue(2)
    • Output: [true, false].
  • Input: k=1, deQueue()
    • Output: [false].

Both handle these well.

Complexity Breakdown

  • Array-Based: Time O(1), Space O(k).
  • Linked List: Time O(1), Space O(k).

Array-based is optimal.

Key Takeaways

  • Array-Based: Pointer magic—smart!
  • Linked List: Node flexibility—clear!
  • Queues: Circular is fun.
  • Python Tip: Arrays rock—see Python Basics.

Final Thoughts: Design That Queue

LeetCode 622: Design Circular Queue in Python is a cool data structure challenge. The array-based approach offers speed and simplicity, while linked lists add flexibility. Want more? Try LeetCode 641: Design Circular Deque or LeetCode 232: Implement Queue using Stacks. Ready to queue up? Head to Solve LeetCode 622 on LeetCode and design that circular queue today!