Skip to content

622. Design Circular Queue

class MyCircularQueue:
    """Design a circular queue (ring buffer) with fixed capacity.

    Problem Statement:
        Implement MyCircularQueue(k), a FIFO queue of capacity k whose last
        position wraps around to the first. Support enQueue, deQueue, Front,
        Rear, isEmpty and isFull. enQueue and deQueue return whether the
        operation succeeded; Front and Rear return -1 on an empty queue.

    Approach:
        Preallocate a buffer of size k and track two values: front, the index
        of the oldest element, and size, the number of stored elements.
        Every other position follows from them modulo k:
            - the rear element is at (front + size - 1) % k;
            - the next free slot is at (front + size) % k.

        enQueue writes into the next free slot and increments size. deQueue
        just advances front by one (mod k) and decrements size; the old value
        is left in the buffer and will be overwritten later.

        Keeping size instead of a rear pointer avoids the ambiguity of
        front == rear meaning either empty or full.

        Example with k = 4, after enQueue 1, 2, 3, 4, deQueue, enQueue 5:
            buffer = [5, 2, 3, 4], front = 1, size = 4
            Front() -> 2, Rear() -> buffer[(1 + 4 - 1) % 4] = 5

    Complexity:
        Time: O(1) for every operation.
        Space: O(k) for the buffer.
    """

    def __init__(self, k: int):
        self._buffer: list[int] = [0] * k
        # Points to the first element to be processed
        self._front = 0
        self._size = 0

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

        insert_idx = (self._front + self._size) % len(self._buffer)
        self._buffer[insert_idx] = value
        self._size += 1

        return True

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

        # The dequeued element, if needed, is self._buffer[self._front]
        self._front = (self._front + 1) % len(self._buffer)
        self._size -= 1

        return True

    def Front(self) -> int:
        if self.isEmpty():
            return -1
        return self._buffer[self._front]

    def Rear(self) -> int:
        if self.isEmpty():
            return -1
        return self._buffer[(self._front + self._size - 1) % len(self._buffer)]

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

    def isFull(self) -> bool:
        return self._size == len(self._buffer)