Types

Circular Linked List

Circular Linked List

Take a regular linked list and change one thing: instead of the last node pointing to null, have it point right back to the first node. That's a circular linked list — a loop with no real end.

It can be built either as a singly-linked loop (one pointer per node) or a doubly-linked loop (two pointers per node). Because the chain never terminates, it's a natural fit for anything that needs to cycle repeatedly, like round-robin scheduling or a circular buffer.

Since there's no fixed "first" or "last" node anymore, you can start traversing from anywhere in the loop and eventually visit every node — handy for problems that are inherently cyclic rather than linear.

Key Property: The last node's next pointer always points back to the first node, creating a continuous loop.

Basic Operations

OperationComplexityDescription
Insertion at HeadO(1)Add new node at beginning, point last node to new head
Insertion at TailO(1)Add new node at end, point it to head (with tail pointer)
Deletion at HeadO(1)Remove first node, update last node's pointer
Deletion by ValueO(n)Traverse list to find and remove specific node
TraversalO(n)Loop through nodes until returning to starting point
SearchO(n)Traverse list to find element

Insertion Process

  1. Create new node with data
  2. If list is empty, set head and tail to new node
  3. Make new node point to itself (circular reference)
  4. For non-empty list, set new node's next to current head
  5. Update tail's next pointer to new node
  6. Move head pointer to new node
ABhead
Existing circular list
XABhead
After inserting X at head

Deletion Process

  1. Check if list is empty
  2. If single node exists, set head and tail to null
  3. For head deletion, update head to head.next
  4. Update tail's next pointer to new head
  5. For middle deletion, find node and update previous node's pointer
  6. Handle special case when deleting last node
XABhead
Current circular list
ABhead
After deleting X (head)

Operation Visualization

OperationList State
Initializationhead → null
insertFirst(10)head → [10] → (points back to head)
insertFirst(20)head → [20] → [10] → (points back to head)
insertFirst(30)head → [30] → [20] → [10] → (points back to head)
deleteFirst()head → [20] → [10] → (points back to head)
delete(10)head → [20] → (points back to itself)

Comparison with Linear Linked List

FeatureLinear Linked ListCircular Linked List
StructureLinear with null terminationCircular with no null
TraversalStops at endContinuous loop
Memory OverheadStandardSame as linear
Boundary DetectionEasy (null check)Requires start reference
Insert/Delete at HeadO(1)O(1)
Implementation ComplexitySimplerMore complex

Applications

  • Operating system round-robin scheduling
  • Multiplayer turn-based games
  • Music/video playlists with repeat functionality
  • Resource allocation in networking
  • Circular buffer implementations
  • Token ring networks

When to Choose: Prefer circular linked lists when you need continuous cycling through elements or when the application naturally follows a circular pattern (like round-robin scheduling).

Advertisement

Visualize Circular Linked List Operations

Empty List

Add nodes to visualize the circular linked list

Test Your Knowledge before moving forward!

Linked List Quiz Challenge

How it works:

  • +1 point for each correct answer
  • 0 points for wrong answers
  • -0.5 point penalty for viewing explanations
  • Earn stars based on your final score (max 5 stars)

Circular Linked List Implementation

// Circular Linked List Implementation in JavaScript
class Node {
  constructor(data) {
    this.data = data;
    this.next = null;
  }
}

class CircularLinkedList {
  constructor() {
    this.head = null;
    this.tail = null;
    this.size = 0;
  }

  // Insert at beginning
  insertFirst(data) {
    const newNode = new Node(data);
    if (!this.head) {
      this.head = newNode;
      this.tail = newNode;
      newNode.next = this.head; // Point to itself
    } else {
      newNode.next = this.head;
      this.head = newNode;
      this.tail.next = this.head; // Update tail's next to new head
    }
    this.size++;
  }

  // Insert at end
  insertLast(data) {
    const newNode = new Node(data);
    if (!this.head) {
      this.head = newNode;
      this.tail = newNode;
      newNode.next = this.head;
    } else {
      this.tail.next = newNode;
      newNode.next = this.head;
      this.tail = newNode;
    }
    this.size++;
  }

  // Insert at index
  insertAt(data, index) {
    if (index < 0 || index > this.size) return;
    if (index === 0) return this.insertFirst(data);
    if (index === this.size) return this.insertLast(data);

    const newNode = new Node(data);
    let current = this.head;
    let count = 0;

    while (count < index - 1) {
      current = current.next;
      count++;
    }

    newNode.next = current.next;
    current.next = newNode;
    this.size++;
  }

  // Remove from beginning
  removeFirst() {
    if (!this.head) return null;
    const removedNode = this.head;
    if (this.size === 1) {
      this.head = null;
      this.tail = null;
    } else {
      this.head = this.head.next;
      this.tail.next = this.head; // Update tail's next to new head
    }
    this.size--;
    return removedNode.data;
  }

  // Remove from end
  removeLast() {
    if (!this.head) return null;
    const removedNode = this.tail;
    if (this.size === 1) {
      this.head = null;
      this.tail = null;
    } else {
      let current = this.head;
      while (current.next !== this.tail) {
        current = current.next;
      }
      current.next = this.head; // Point new tail to head
      this.tail = current;
    }
    this.size--;
    return removedNode.data;
  }

  // Remove at index
  removeAt(index) {
    if (index < 0 || index >= this.size) return null;
    if (index === 0) return this.removeFirst();
    if (index === this.size - 1) return this.removeLast();

    let current = this.head;
    let count = 0;
    while (count < index - 1) {
      current = current.next;
      count++;
    }
    const removedNode = current.next;
    current.next = removedNode.next;
    this.size--;
    return removedNode.data;
  }

  // Get at index
  getAt(index) {
    if (index < 0 || index >= this.size) return null;
    let current = this.head;
    let count = 0;
    while (count < index) {
      current = current.next;
      count++;
    }
    return current.data;
  }

  // Clear list
  clear() {
    this.head = null;
    this.tail = null;
    this.size = 0;
  }

  // Print list
  print() {
    if (!this.head) {
      console.log("List is empty");
      return;
    }
    let current = this.head;
    let result = "";
    do {
      result += current.data + " -> ";
      current = current.next;
    } while (current !== this.head);
    result += "(head)";
    console.log(result);
  }

  // Check if list is circular
  isCircular() {
    if (!this.head) return true;
    let slow = this.head;
    let fast = this.head.next;
    while (fast && fast.next) {
      if (slow === fast) return true;
      slow = slow.next;
      fast = fast.next.next;
    }
    return false;
  }
}

// Usage Example
const cll = new CircularLinkedList();
cll.insertFirst(100);
cll.insertFirst(200);
cll.insertLast(300);
cll.insertAt(500, 1);
cll.print(); // 200 -> 500 -> 100 -> 300 -> (head)
cll.removeAt(2);
console.log(cll.getAt(1)); // 500
console.log("Is circular:", cll.isCircular()); // true

Done With the Learning

Mark Circular Linked List as done and view it on your dashboard