Bird
Raised Fist0
DSA Javascriptprogramming~15 mins

Circular Queue Implementation Using Array in DSA Javascript - Deep Dive

Choose your learning style10 modes available
Overview - Circular Queue Implementation Using Array
What is it?
A circular queue is a special type of queue where the last position is connected back to the first position, forming a circle. It uses an array to store elements and two pointers to track the front and rear positions. This structure allows efficient use of space by reusing empty slots created after removing elements. It works like a normal queue but wraps around when it reaches the end of the array.
Why it matters
Without circular queues, simple queues using arrays waste space when elements are removed from the front, as the empty spots cannot be reused. Circular queues solve this by reusing those spots, making memory use efficient and operations faster. This is important in real-world systems like CPU scheduling, network buffering, and resource management where fixed-size buffers are common.
Where it fits
Before learning circular queues, you should understand basic queues and arrays. After mastering circular queues, you can explore linked list implementations of queues, priority queues, and more complex data structures like deques and ring buffers.
Mental Model
Core Idea
A circular queue uses a fixed-size array where the end connects back to the start, allowing continuous enqueue and dequeue operations without wasting space.
Think of it like...
Imagine a round table with numbered seats. People sit in order, and when the last seat is taken, the next person sits back at the first seat if it's empty. This way, seats are reused efficiently without needing more chairs.
Front and Rear pointers move around a circular array:

  [0] [1] [2] [3] [4]
   ↑                 
  Rear               

After some operations:

  [A] [B] [ ] [ ] [ ]
       ↑           ↑
     Front        Rear

When Rear reaches the end, it wraps to start:

  [A] [B] [C] [D] [E]
       ↑           ↑
     Front        Rear

If Front moves forward, empty spots are reused.
Build-Up - 7 Steps
1
FoundationUnderstanding Basic Queue Concept
šŸ¤”
Concept: Learn what a queue is and how it works with simple enqueue and dequeue operations.
A queue is like a line of people waiting. The first person to get in line is the first to be served (First-In-First-Out). We add people at the rear and remove from the front. Using an array, we keep track of front and rear indexes to add and remove elements.
Result
You can add elements at the rear and remove from the front in order.
Understanding the simple queue behavior is essential before adding complexity like circular wrapping.
2
FoundationLimitations of Simple Array Queue
šŸ¤”
Concept: See why a normal array queue wastes space after some dequeues.
In a simple array queue, when you remove elements from the front, those spots become empty but can't be reused. For example, if the array size is 5 and you enqueue 5 elements, then dequeue 3, the front moves forward but rear stays at the end. You cannot add new elements even though there is space at the start.
Result
Queue appears full even when there is empty space at the beginning.
Recognizing this waste of space motivates the need for circular queues.
3
IntermediateIntroducing Circular Queue Concept
šŸ¤”Before reading on: do you think the rear pointer moves only forward or can it wrap around to the start? Commit to your answer.
Concept: The rear pointer wraps to the start of the array when it reaches the end, reusing empty spots.
In a circular queue, when rear reaches the last index of the array, it moves back to index 0 if that spot is free. Similarly, front moves forward and wraps around. This way, the queue uses the array as a circle, efficiently reusing space.
Result
You can enqueue and dequeue continuously without running out of space prematurely.
Understanding pointer wrapping is key to efficient queue operations in fixed-size arrays.
4
IntermediateManaging Front and Rear Pointers
šŸ¤”Before reading on: do you think front == rear means queue is empty or full? Commit to your answer.
Concept: Use pointer positions carefully to distinguish between empty and full states.
In circular queues, front == rear can mean either empty or full. To avoid confusion, one common method is to leave one slot empty. The queue is full when (rear + 1) % size == front. The queue is empty when front == rear. This helps manage the queue state clearly.
Result
You can correctly detect when the queue is full or empty.
Knowing how to differentiate full and empty states prevents bugs and infinite loops.
5
IntermediateImplementing Enqueue and Dequeue Operations
šŸ¤”
Concept: Learn how to add and remove elements with pointer updates and wrap-around logic.
To enqueue: check if queue is full. If not, place element at rear, then move rear = (rear + 1) % size. To dequeue: check if queue is empty. If not, remove element at front, then move front = (front + 1) % size. This keeps the queue circular and efficient.
Result
You can perform queue operations without wasting space or errors.
Implementing these operations correctly is the foundation for using circular queues in practice.
6
AdvancedComplete Circular Queue Code in JavaScript
šŸ¤”Before reading on: do you think the code should check for full queue before enqueue or after? Commit to your answer.
Concept: Putting all ideas together into runnable JavaScript code with comments.
class CircularQueue { constructor(size) { this.size = size + 1; // one extra space to distinguish full/empty this.queue = new Array(this.size); this.front = 0; this.rear = 0; } isEmpty() { return this.front === this.rear; } isFull() { return (this.rear + 1) % this.size === this.front; } enqueue(value) { if (this.isFull()) { console.log('Queue is full'); return false; } this.queue[this.rear] = value; this.rear = (this.rear + 1) % this.size; return true; } dequeue() { if (this.isEmpty()) { console.log('Queue is empty'); return null; } const value = this.queue[this.front]; this.front = (this.front + 1) % this.size; return value; } printQueue() { let i = this.front; let result = ''; while (i !== this.rear) { result += this.queue[i] + ' -> '; i = (i + 1) % this.size; } result += 'null'; console.log(result); } } // Example usage: const cq = new CircularQueue(5); cq.enqueue(10); cq.enqueue(20); cq.enqueue(30); cq.printQueue(); // 10 -> 20 -> 30 -> null cq.dequeue(); cq.printQueue(); // 20 -> 30 -> null cq.enqueue(40); cq.enqueue(50); cq.enqueue(60); cq.printQueue(); // 20 -> 30 -> 40 -> 50 -> 60 -> null cq.enqueue(70); // Queue is full
Result
Printed queue states: 10 -> 20 -> 30 -> null 20 -> 30 -> null 20 -> 30 -> 40 -> 50 -> 60 -> null And message: Queue is full
Seeing the full code and output confirms how circular queues work in practice and how to handle edge cases.
7
ExpertPerformance and Memory Efficiency Insights
šŸ¤”Before reading on: do you think circular queues always improve performance over linked lists? Commit to your answer.
Concept: Understand when circular queues are better or worse than other queue types.
Circular queues use fixed-size arrays, so they have constant time enqueue and dequeue with low overhead and good cache performance. However, they have a fixed capacity and can waste one slot to distinguish full/empty. Linked lists allow dynamic size but have extra memory for pointers and slower cache access. Circular queues are ideal for fixed buffer scenarios like embedded systems or real-time processing.
Result
You know when to choose circular queues for best performance and memory use.
Knowing trade-offs helps pick the right queue type for your application.
Under the Hood
Internally, the circular queue uses modular arithmetic to wrap the front and rear pointers around the fixed-size array. This means when a pointer reaches the array's end, it resets to zero. The queue keeps one slot empty to distinguish between full and empty states, avoiding ambiguity. Enqueue places an element at rear and moves rear forward; dequeue removes from front and moves front forward. This pointer movement and wrap-around create a continuous circular buffer.
Why designed this way?
Circular queues were designed to solve the problem of wasted space in simple array queues. The fixed-size array is efficient for memory and speed, but without wrap-around, it becomes unusable after some dequeues. The design choice to keep one slot empty simplifies full/empty detection, avoiding complex state tracking. Alternatives like linked lists offer dynamic size but with more overhead, so circular queues balance simplicity and efficiency.
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│   Array     │
│ 0 1 2 3 4  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
  ↑       ↑
 Front   Rear

Pointers move:
Front -> (Front + 1) % size
Rear  -> (Rear + 1) % size

Full condition: (Rear + 1) % size == Front
Empty condition: Front == Rear
Myth Busters - 3 Common Misconceptions
Quick: Does front == rear always mean the queue is empty? Commit to yes or no.
Common Belief:If front and rear pointers are equal, the queue must be empty.
Tap to reveal reality
Reality:Front == rear can mean either the queue is empty or full, depending on implementation. Circular queues usually keep one slot empty to distinguish these states.
Why it matters:Misinterpreting this causes incorrect enqueue or dequeue operations, leading to data loss or infinite loops.
Quick: Can a circular queue dynamically grow its size like a linked list? Commit to yes or no.
Common Belief:Circular queues can grow dynamically to hold more elements.
Tap to reveal reality
Reality:Circular queues have a fixed size defined at creation and cannot grow dynamically. To handle more elements, a new larger queue must be created and data copied.
Why it matters:Assuming dynamic growth leads to buffer overflow errors and crashes in fixed-size systems.
Quick: Is it okay to fill all slots in a circular queue without leaving any empty? Commit to yes or no.
Common Belief:You can fill all slots in the array without any empty space in a circular queue.
Tap to reveal reality
Reality:One slot must remain empty to differentiate full and empty states. Filling all slots causes ambiguity and errors.
Why it matters:Ignoring this leads to incorrect full/empty detection and bugs in queue operations.
Expert Zone
1
The choice to keep one slot empty is a simple but subtle design that avoids extra variables for size tracking.
2
Pointer arithmetic using modulo is efficient but can cause off-by-one errors if not carefully implemented.
3
In high-performance systems, circular queues leverage CPU cache locality better than linked lists, improving speed.
When NOT to use
Avoid circular queues when you need a dynamically sized queue or when the maximum size is unknown. Use linked lists or dynamic arrays instead. Also, if you need priority ordering, use priority queues or heaps.
Production Patterns
Circular queues are widely used in embedded systems for buffering sensor data, in operating systems for task scheduling, and in network programming for packet buffering. They are often implemented in low-level languages for speed and memory control.
Connections
Ring Buffer
Circular queue is a type of ring buffer specialized for queue operations.
Understanding circular queues helps grasp ring buffers used in audio processing and streaming where continuous data flow is critical.
Modulo Arithmetic
Circular queues rely on modulo arithmetic to wrap pointers around the array.
Knowing modulo arithmetic from math helps understand pointer wrap-around logic in circular queues.
Traffic Roundabouts
Circular queues conceptually resemble traffic roundabouts where cars enter and exit in order without stopping.
This analogy from traffic engineering shows how circular flow manages continuous movement efficiently, similar to data in circular queues.
Common Pitfalls
#1Confusing full and empty states when front == rear.
Wrong approach:if (front === rear) { // assume queue is empty dequeue(); }
Correct approach:if ((rear + 1) % size === front) { // queue is full enqueue(); } else if (front === rear) { // queue is empty dequeue(); }
Root cause:Not reserving one slot or not checking full condition separately causes ambiguity.
#2Not using modulo operation for pointer wrap-around.
Wrong approach:rear = rear + 1; // without modulo if (rear == size) rear = 0;
Correct approach:rear = (rear + 1) % size;
Root cause:Manual wrap-around logic is error-prone and less clear than modulo arithmetic.
#3Trying to enqueue when queue is full without checking.
Wrong approach:enqueue(value) { queue[rear] = value; rear = (rear + 1) % size; }
Correct approach:enqueue(value) { if (isFull()) { console.log('Queue is full'); return false; } queue[rear] = value; rear = (rear + 1) % size; return true; }
Root cause:Skipping full check causes overwriting data and corrupting queue state.
Key Takeaways
Circular queues efficiently reuse array space by connecting the end back to the start using modular arithmetic.
Keeping one slot empty is a simple trick to distinguish between full and empty states and avoid ambiguity.
Correctly managing front and rear pointers with wrap-around logic is essential for reliable queue operations.
Circular queues are ideal for fixed-size buffer scenarios where memory efficiency and speed matter.
Understanding circular queues builds a foundation for advanced data structures like ring buffers and real-time systems.

Practice

(1/5)
1. What is the main advantage of using a circular queue over a simple linear queue when implemented using an array?
easy
A. It automatically sorts the elements in the queue.
B. It efficiently uses all available space by wrapping around the array.
C. It allows storing different data types in the queue.
D. It uses less memory than a linear queue.

Solution

  1. Step 1: Understand linear queue limitation

    In a linear queue using an array, once the rear reaches the end, no more elements can be added even if there is free space at the front.
  2. Step 2: Circular queue advantage

    A circular queue connects the end of the array back to the front, allowing reuse of free space and efficient use of the array.
  3. Final Answer:

    It efficiently uses all available space by wrapping around the array. -> Option B
  4. Quick Check:

    Circular queue = efficient space use [OK]
Hint: Circular queue reuses space by wrapping indices [OK]
Common Mistakes:
  • Thinking circular queue sorts elements
  • Believing it uses less memory than linear queue
  • Assuming it allows mixed data types automatically
2. Which of the following is the correct way to check if a circular queue is full when using an array of size size with front and rear pointers?
easy
A. if (rear === size - 1)
B. if (rear === front)
C. if ((rear + 1) % size === front)
D. if (front === -1)

Solution

  1. Step 1: Understand full condition in circular queue

    The queue is full when the next position of rear (rear + 1 modulo size) equals front, meaning no space to insert.
  2. Step 2: Check each option

    if ((rear + 1) % size === front) correctly uses modulo to wrap around and compare with front. Others do not correctly represent full condition.
  3. Final Answer:

    if ((rear + 1) % size === front) -> Option C
  4. Quick Check:

    Full condition = (rear + 1) % size === front [OK]
Hint: Use modulo to wrap rear and compare with front [OK]
Common Mistakes:
  • Checking rear === front for full (actually empty condition)
  • Ignoring modulo wrap-around
  • Using front === -1 to check full
3. Consider the following JavaScript code for a circular queue of size 5. What will be the output after these operations?
const size = 5;
let queue = new Array(size);
let front = -1, rear = -1;

function enqueue(value) {
  if ((rear + 1) % size === front) {
    console.log('Queue is full');
    return;
  }
  if (front === -1) front = 0;
  rear = (rear + 1) % size;
  queue[rear] = value;
}

enqueue(10);
enqueue(20);
enqueue(30);
enqueue(40);
enqueue(50); // This enqueues successfully (queue now full)
console.log(queue);
medium
A. [10, 20, 30, 40, 50]
B. [10, 20, 30, 40, undefined]
C. [10, 20, 30, 40, null]
D. [undefined, 10, 20, 30, 40]

Solution

  1. Step 1: Trace enqueue operations

    Initial: front=-1, rear=-1.
    enqueue(10): front=0, rear=0, queue[0]=10.
    enqueue(20): rear=1, queue[1]=20.
    enqueue(30): rear=2, queue[2]=30.
    enqueue(40): rear=3, queue[3]=40.
    enqueue(50): (rear+1)%5 = 4 === front 0? No, rear=4, queue[4]=50. No full printed.
  2. Step 2: Verify output

    5 enqueues succeed for size 5. console.log(queue) shows [10, 20, 30, 40, 50].
  3. Final Answer:

    [10, 20, 30, 40, 50] -> Option A
  4. Quick Check:

    Full after exactly size enqueues [OK]
Hint: Check modulo full condition carefully to trace enqueue [OK]
Common Mistakes:
  • Thinking queue full after 4 enqueues (5th fails)
  • Confusing undefined and null values
  • Ignoring front and rear initial values
4. The following code snippet for dequeue operation in a circular queue has a bug. What is the bug?
function dequeue() {
  if (front === -1) {
    console.log('Queue is empty');
    return null;
  }
  let value = queue[front];
  if (front === rear) {
    front = rear = -1;
  } else {
    front = front + 1;
  }
  return value;
}
medium
A. front should be updated using modulo to wrap around
B. rear should be incremented instead of front
C. The empty check should be rear === -1
D. The function should not return null when empty

Solution

  1. Step 1: Analyze front update in dequeue

    When removing an element, front moves forward. Since queue is circular, front must wrap using modulo size.
  2. Step 2: Identify bug in front update

    Code uses front = front + 1 without modulo, so front can go beyond array size causing errors.
  3. Final Answer:

    front should be updated using modulo to wrap around -> Option A
  4. Quick Check:

    Update front with modulo to avoid overflow [OK]
Hint: Always use modulo when moving front or rear in circular queue [OK]
Common Mistakes:
  • Incrementing rear instead of front on dequeue
  • Checking empty condition incorrectly
  • Ignoring modulo wrap-around
5. You have a circular queue of size 4 implemented using an array. Initially, front = -1 and rear = -1. You perform these operations:
enqueue(1); enqueue(2); dequeue(); enqueue(3); enqueue(4); dequeue(); enqueue(5);

What is the final state of the queue array?
hard
A. [undefined, undefined, 3, 5]
B. [5, 2, 3, 4]
C. [undefined, 2, 3, 5]
D. [5, undefined, 3, 4]

Solution

  1. Step 1: Trace each operation step-by-step

    Start: front=-1, rear=-1, queue=[undefined,undefined,undefined,undefined]
    enqueue(1): front=0, rear=0, queue=[1,undefined,undefined,undefined]
    enqueue(2): rear=1, queue=[1,2,undefined,undefined]
    dequeue(): value=queue[0]=1, front=1 (no clear), queue=[1,2,undefined,undefined]
    enqueue(3): rear=2, queue=[1,2,3,undefined]
    enqueue(4): rear=3, queue=[1,2,3,4]
    dequeue(): value=queue[1]=2, front=2 (no clear), queue=[1,2,3,4]
    enqueue(5): rear=0, queue[0]=5, queue=[5,2,3,4]
  2. Step 2: Final queue array state

    queue=[5,2,3,4]
    front=2, rear=0. Dequeue does not clear slots; old values persist until overwritten. Position 1 skipped.
  3. Final Answer:

    [5, undefined, 3, 4] -> Option D
  4. Quick Check:

    Wrap-around enqueue updates index 0 after dequeues [OK]
Hint: Track front and rear carefully with modulo wrap [OK]
Common Mistakes:
  • Not wrapping rear index on enqueue
  • Assuming dequeue clears array slots to undefined
  • Confusing front and rear after dequeues
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard