title: Design Circular Queue
slug: design-circular-queue
difficulty: medium
leetcode_id: 622
leetcode_url: https://leetcode.com/problems/design-circular-queue/
categories:
  - arrays
  - queue
patterns:
  - two-pointers

function_signature: "class MyCircularQueue: ..."

test_cases:
  visible:
    - input:
        operations: ["MyCircularQueue", "enQueue", "enQueue", "enQueue", "enQueue", "Rear", "isFull", "deQueue", "enQueue", "Rear"]
        arguments: [[3], [1], [2], [3], [4], [], [], [], [4], []]
      expected: [null, true, true, true, false, 3, true, true, true, 4]
  hidden:
    - input:
        operations: ["MyCircularQueue", "isEmpty", "enQueue", "isEmpty", "Front", "deQueue", "isEmpty"]
        arguments: [[1], [], [5], [], [], [], []]
      expected: [null, true, true, false, 5, true, true]
    - input:
        operations: ["MyCircularQueue", "enQueue", "enQueue", "Front", "Rear", "deQueue", "Front"]
        arguments: [[2], [1], [2], [], [], [], []]
      expected: [null, true, true, 1, 2, true, 2]
    - input:
        operations: ["MyCircularQueue", "deQueue", "Front", "Rear"]
        arguments: [[3], [], [], []]
      expected: [null, false, -1, -1]
    - input:
        operations: ["MyCircularQueue", "enQueue", "enQueue", "enQueue", "deQueue", "enQueue", "Front", "Rear"]
        arguments: [[3], [1], [2], [3], [], [4], [], []]
      expected: [null, true, true, true, true, true, 2, 4]
    - input:
        operations: ["MyCircularQueue", "enQueue", "deQueue", "enQueue", "deQueue", "enQueue", "Front", "Rear"]
        arguments: [[1], [10], [], [20], [], [30], [], []]
      expected: [null, true, true, true, true, true, 30, 30]
    - input:
        operations: ["MyCircularQueue", "isFull", "enQueue", "enQueue", "isFull", "enQueue", "isFull"]
        arguments: [[2], [], [1], [2], [], [3], []]
      expected: [null, false, true, true, true, false, true]

description: |
  Design your implementation of the circular queue. The circular queue is a linear data structure in which the operations are performed based on FIFO (First In First Out) principle, and the last position is connected back to the first position to make a circle. It is also called "Ring Buffer".

  One of the benefits of the circular queue is that we can make use of the spaces in front of the queue. In a normal queue, once the queue becomes full, we cannot insert the next element even if there is a space in front of the queue. But using the circular queue, we can use the space to store new values.

  Implement the `MyCircularQueue` class:

  - `MyCircularQueue(k)` Initializes the object with the size of the queue to be `k`.
  - `int Front()` Gets the front item from the queue. If the queue is empty, return `-1`.
  - `int Rear()` Gets the last item from the queue. If the queue is empty, return `-1`.
  - `boolean enQueue(int value)` Inserts an element into the circular queue. Return `true` if the operation is successful.
  - `boolean deQueue()` Deletes an element from the circular queue. Return `true` if the operation is successful.
  - `boolean isEmpty()` Checks whether the circular queue is empty or not.
  - `boolean isFull()` Checks whether the circular queue is full or not.

  You must solve the problem without using the built-in queue data structure in your programming language.

constraints: |
  - `1 <= k <= 1000`
  - `0 <= value <= 1000`
  - At most `3000` calls will be made to `enQueue`, `deQueue`, `Front`, `Rear`, `isEmpty`, and `isFull`.

examples:
  - input: |
      ["MyCircularQueue", "enQueue", "enQueue", "enQueue", "enQueue", "Rear", "isFull", "deQueue", "enQueue", "Rear"]
      [[3], [1], [2], [3], [4], [], [], [], [4], []]
    output: "[null, true, true, true, false, 3, true, true, true, 4]"
    explanation: |
      MyCircularQueue myCircularQueue = new MyCircularQueue(3);
      myCircularQueue.enQueue(1); // return True
      myCircularQueue.enQueue(2); // return True
      myCircularQueue.enQueue(3); // return True, queue is now [1, 2, 3]
      myCircularQueue.enQueue(4); // return False, queue is full
      myCircularQueue.Rear();     // return 3
      myCircularQueue.isFull();   // return True
      myCircularQueue.deQueue();  // return True, removes 1, queue is [2, 3]
      myCircularQueue.enQueue(4); // return True, queue is now [2, 3, 4]
      myCircularQueue.Rear();     // return 4

explanation:
  intuition: |
    Imagine a circular track where runners line up at different positions. When the track is full and someone at the front leaves, a new runner can take that vacated spot — the track "wraps around" from the end back to the beginning.

    A circular queue works the same way. Unlike a standard array-based queue where you might shift all elements when dequeuing (expensive!) or waste space at the front after dequeuing, a circular queue uses **modular arithmetic** to wrap indices around. When the `rear` pointer reaches the end of the array, it wraps back to index `0` if there's space available.

    The key insight is that we need to track two things:
    1. **Where the front of the queue is** (for dequeue and peek operations)
    2. **How many elements are currently in the queue** (to know if it's empty or full)

    With these two pieces of information, we can compute where the rear is, and we can use the modulo operator (`%`) to wrap indices around the fixed-size array.

  approach: |
    We implement the circular queue using a **fixed-size array** with two pointers and a count:

    **Step 1: Initialise the data structure**

    - `data`: A fixed-size array of length `k` to store elements
    - `head`: Points to the front element (where we dequeue from)
    - `count`: Tracks the current number of elements in the queue

    &nbsp;

    **Step 2: Implement enQueue (insert at rear)**

    - If the queue is full, return `false`
    - Calculate the rear index: `(head + count) % k`
    - Place the new element at this position
    - Increment `count`
    - Return `true`

    &nbsp;

    **Step 3: Implement deQueue (remove from front)**

    - If the queue is empty, return `false`
    - Move `head` forward: `head = (head + 1) % k`
    - Decrement `count`
    - Return `true`

    &nbsp;

    **Step 4: Implement Front and Rear**

    - `Front()`: Return `data[head]` if not empty, else `-1`
    - `Rear()`: Calculate rear index as `(head + count - 1) % k`, return that element if not empty

    &nbsp;

    **Step 5: Implement isEmpty and isFull**

    - `isEmpty()`: Return `count == 0`
    - `isFull()`: Return `count == k`

    &nbsp;

    The modulo operation is the magic that makes the "circular" behaviour work — it ensures indices wrap around when they exceed the array bounds.

  common_pitfalls:
    - title: Confusing Head and Tail Management
      description: |
        A common mistake is maintaining both `head` and `tail` pointers and struggling to differentiate between empty and full states. With two pointers, both `head == tail` could mean empty OR full!

        Using a `count` variable instead of a `tail` pointer eliminates this ambiguity entirely. You always know the state: `count == 0` means empty, `count == k` means full.
      wrong_approach: "Using head and tail pointers without a count"
      correct_approach: "Use head pointer + count, compute tail when needed"

    - title: Off-by-One Errors in Rear Calculation
      description: |
        When computing the rear index, remember that `rear` points to the *last element*, not the next empty slot. The formula is:

        `rear = (head + count - 1) % k`

        Not `(head + count) % k`, which would be the next insertion point.

        For example, if `head = 0`, `count = 3`, and `k = 5`, the rear is at index `2`, not `3`.
      wrong_approach: "rear = (head + count) % k"
      correct_approach: "rear = (head + count - 1) % k"

    - title: Forgetting to Handle Empty Queue Edge Cases
      description: |
        When the queue is empty, `Front()` and `Rear()` should return `-1`, not cause an index error or return garbage.

        Always check `isEmpty()` before accessing elements:

        ```python
        def Front(self):
            if self.isEmpty():
                return -1
            return self.data[self.head]
        ```
      wrong_approach: "Directly accessing data[head] without checking empty"
      correct_approach: "Always check isEmpty() before accessing elements"

  key_takeaways:
    - "**Ring buffer pattern**: Circular queues are foundational for streaming data, bounded buffers, and producer-consumer scenarios"
    - "**Modular arithmetic**: The `%` operator is the key to wrapping indices around a fixed-size array"
    - "**Count vs. two pointers**: Tracking `count` separately simplifies empty/full detection and avoids ambiguous states"
    - "**O(1) operations**: All operations are constant time, making this ideal for high-throughput scenarios"

  time_complexity: "O(1). All operations (`enQueue`, `deQueue`, `Front`, `Rear`, `isEmpty`, `isFull`) execute in constant time."
  space_complexity: "O(k). We use a fixed-size array of length `k` to store up to `k` elements."

solutions:
  - approach_name: Array with Head and Count
    is_optimal: true
    code: |
      class MyCircularQueue:
          def __init__(self, k: int):
              # Fixed-size array to store elements
              self.data = [0] * k
              # Index of the front element
              self.head = 0
              # Current number of elements
              self.count = 0
              # Maximum capacity
              self.capacity = k

          def enQueue(self, value: int) -> bool:
              # Cannot insert if queue is full
              if self.isFull():
                  return False

              # Calculate rear position using modular arithmetic
              # This wraps around when we reach the end of the array
              rear = (self.head + self.count) % self.capacity
              self.data[rear] = value
              self.count += 1
              return True

          def deQueue(self) -> bool:
              # Cannot remove if queue is empty
              if self.isEmpty():
                  return False

              # Move head forward (with wrap-around)
              self.head = (self.head + 1) % self.capacity
              self.count -= 1
              return True

          def Front(self) -> int:
              # Return -1 for empty queue
              if self.isEmpty():
                  return -1
              return self.data[self.head]

          def Rear(self) -> int:
              # Return -1 for empty queue
              if self.isEmpty():
                  return -1
              # Rear is at (head + count - 1), wrapped around
              rear = (self.head + self.count - 1) % self.capacity
              return self.data[rear]

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

          def isFull(self) -> bool:
              return self.count == self.capacity
    explanation: |
      **Time Complexity:** O(1) — All operations are constant time with no loops.

      **Space Complexity:** O(k) — Fixed array size determined at initialization.

      This implementation uses a single `head` pointer combined with a `count` variable. The rear position is computed on-demand using `(head + count) % capacity`. This approach is cleaner than maintaining separate head and tail pointers because the `count` variable unambiguously indicates whether the queue is empty or full.

  - approach_name: Linked List Implementation
    is_optimal: false
    code: |
      class ListNode:
          def __init__(self, val: int):
              self.val = val
              self.next = None

      class MyCircularQueue:
          def __init__(self, k: int):
              self.capacity = k
              self.count = 0
              # Head and tail pointers for the linked list
              self.head = None
              self.tail = None

          def enQueue(self, value: int) -> bool:
              if self.isFull():
                  return False

              new_node = ListNode(value)

              if self.isEmpty():
                  # First element: both head and tail point to it
                  self.head = new_node
                  self.tail = new_node
              else:
                  # Append to tail
                  self.tail.next = new_node
                  self.tail = new_node

              self.count += 1
              return True

          def deQueue(self) -> bool:
              if self.isEmpty():
                  return False

              # Move head to next node
              self.head = self.head.next
              self.count -= 1

              # If queue becomes empty, reset tail too
              if self.isEmpty():
                  self.tail = None

              return True

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

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

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

          def isFull(self) -> bool:
              return self.count == self.capacity
    explanation: |
      **Time Complexity:** O(1) — All operations are constant time.

      **Space Complexity:** O(k) — At most `k` nodes in the linked list.

      This alternative uses a singly linked list instead of an array. While it also achieves O(1) operations, it has higher memory overhead due to node objects and pointers. The array-based approach is generally preferred for its better cache locality and lower memory footprint. However, the linked list approach demonstrates that the "circular" concept is about the logical structure, not necessarily a physical ring in memory.
