Bird
Raised Fist0
DSA Javascriptprogramming

Circular Queue Implementation Using Array in DSA Javascript

Choose your learning style10 modes available
Mental Model
A circular queue uses a fixed-size array where the end connects back to the start, so we reuse empty spaces efficiently.
Analogy: Imagine a round table with seats numbered in a circle. When you reach the last seat, you go back to the first seat to sit if it's empty.
Front -> [ ] [ ] [ ] [ ] [ ] ← Rear
Indexes: 0   1   2   3   4
Dry Run Walkthrough
Input: Create circular queue of size 5, enqueue 3 elements (10, 20, 30), dequeue 1 element, enqueue 2 elements (40, 50), then enqueue 1 element (60) to test wrap-around
Goal: Show how elements wrap around in the array and how front and rear pointers move
Step 1: Enqueue 10
Queue: [10] [ ] [ ] [ ] [ ]
Front=0, Rear=0
Why: First element goes to index 0, front and rear both point here
Step 2: Enqueue 20
Queue: [10] [20] [ ] [ ] [ ]
Front=0, Rear=1
Why: Rear moves forward to index 1 to add new element
Step 3: Enqueue 30
Queue: [10] [20] [30] [ ] [ ]
Front=0, Rear=2
Why: Rear moves forward to index 2 to add new element
Step 4: Dequeue one element (10)
Queue: [ ] [20] [30] [ ] [ ]
Front=1, Rear=2
Why: Front moves forward to index 1, element 10 is removed logically
Step 5: Enqueue 40
Queue: [ ] [20] [30] [40] [ ]
Front=1, Rear=3
Why: Rear moves forward to index 3 to add new element
Step 6: Enqueue 50
Queue: [ ] [20] [30] [40] [50]
Front=1, Rear=4
Why: Rear moves forward to index 4 to add new element
Step 7: Enqueue 60 (wrap-around)
Queue: [60] [20] [30] [40] [50]
Front=1, Rear=0
Why: Rear wraps around to index 0 because array end reached
Result:
Queue: [60] [20] [30] [40] [50]
Front=1, Rear=0
Annotated Code
DSA Javascript
class CircularQueue {
  constructor(size) {
    this.size = size;
    this.queue = new Array(size).fill(null);
    this.front = -1;
    this.rear = -1;
  }

  isFull() {
    return ((this.rear + 1) % this.size) === this.front;
  }

  isEmpty() {
    return this.front === -1;
  }

  enqueue(value) {
    if (this.isFull()) {
      console.log('Queue is full');
      return;
    }
    if (this.isEmpty()) {
      this.front = 0;
    }
    this.rear = (this.rear + 1) % this.size; // move rear forward circularly
    this.queue[this.rear] = value; // insert value at rear
  }

  dequeue() {
    if (this.isEmpty()) {
      console.log('Queue is empty');
      return null;
    }
    const value = this.queue[this.front];
    this.queue[this.front] = null; // clear slot
    if (this.front === this.rear) {
      this.front = -1; // queue becomes empty
      this.rear = -1;
    } else {
      this.front = (this.front + 1) % this.size; // move front forward circularly
    }
    return value;
  }

  printQueue() {
    if (this.isEmpty()) {
      console.log('Queue is empty');
      return;
    }
    let result = '';
    let i = this.front;
    while (true) {
      result += this.queue[i] + ' ';
      if (i === this.rear) break;
      i = (i + 1) % this.size;
    }
    console.log(result.trim());
  }
}

// Driver code
const cq = new CircularQueue(5);
cq.enqueue(10);
cq.enqueue(20);
cq.enqueue(30);
cq.printQueue();

cq.dequeue();
cq.printQueue();

cq.enqueue(40);
cq.enqueue(50);
cq.printQueue();

cq.enqueue(60); // should wrap around
cq.printQueue();
this.rear = (this.rear + 1) % this.size; // move rear forward circularly
advance rear pointer circularly to next slot
this.queue[this.rear] = value; // insert value at rear
place new element at rear position
const value = this.queue[this.front];
store front element to return
this.queue[this.front] = null; // clear slot
remove element logically from front
if (this.front === this.rear) { this.front = -1; this.rear = -1; }
reset pointers when queue becomes empty
this.front = (this.front + 1) % this.size; // move front forward circularly
advance front pointer circularly after dequeue
while (true) { ... i = (i + 1) % this.size; }
iterate circularly from front to rear to print queue
OutputSuccess
10 20 30 20 30 20 30 40 50 20 30 40 50 60
Complexity Analysis
Time: O(1) for enqueue and dequeue because pointers move in constant time without shifting elements
Space: O(n) because we use a fixed-size array of size n to store elements
vs Alternative: Compared to a normal queue using array shifting (O(n) dequeue), circular queue avoids costly shifts by reusing space circularly
Edge Cases
Enqueue when queue is full
Prints 'Queue is full' and does not add element
DSA Javascript
if (this.isFull()) { console.log('Queue is full'); return; }
Dequeue when queue is empty
Prints 'Queue is empty' and returns null
DSA Javascript
if (this.isEmpty()) { console.log('Queue is empty'); return null; }
Dequeue last element
Resets front and rear to -1 indicating empty queue
DSA Javascript
if (this.front === this.rear) { this.front = -1; this.rear = -1; }
When to Use This Pattern
When you need a fixed-size queue that efficiently reuses space without shifting elements, use the circular queue pattern because it wraps pointers around the array.
Common Mistakes
Mistake: Not wrapping front or rear pointers using modulo causes index out of bounds errors
Fix: Always update pointers using (pointer + 1) % size to wrap around
Mistake: Not resetting front and rear to -1 when queue becomes empty leads to incorrect empty checks
Fix: Reset front and rear to -1 when last element is dequeued
Summary
Implements a queue using a fixed-size array where front and rear wrap around to reuse space.
Use when you want a queue with fixed capacity and efficient enqueue/dequeue without shifting elements.
The key is to move front and rear pointers circularly using modulo to reuse array slots.

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