Data Structures and Algorithms

Understanding Circular Linked Lists in Data Structures

Linked lists form the backbone of many data structures, connecting nodes with data and pointers to the next in line. Among their intriguing variants, circular linked lists stand out by looping the last node back to the first, creating an endless cycle. This guide dives deep into circular linked lists, covering their types, operations, implementations, and practical uses. Whether you’re preparing for coding interviews or building real-world applications, mastering this structure can give you an edge. Stay ahead of the curve, sign up for our newsletter to unlock free courses and get the latest updates on data structures and more!

What is a Circular Linked List?

A circular linked list transforms the traditional linked list by connecting the last node to the first, forming a continuous loop. Unlike linear linked lists that end with a null pointer, this structure allows traversal to cycle indefinitely from any starting point. This unique trait makes it a powerful tool for tasks requiring repetitive or circular processing, such as scheduling or resource management. 

Curious about other data structures? Explore our DSA course for comprehensive learning.

What is a Circular Linked List?

Types of Circular Linked Lists

Circular linked lists come in two distinct types, each with its own strengths:

Circular Singly Linked List

Here, each node holds data and a pointer to the next node, with the last node linking back to the first. This creates a one-way loop, perfect for simple cyclic operations where forward traversal suffices.

Types of Circular Linked Lists

Circular Doubly Linked List

More complex, this type equips nodes with two pointers: one to the next node and one to the previous. The last node connects to the first, and the first to the last, enabling bidirectional traversal. This flexibility suits advanced applications but demands careful pointer management.

Circular Doubly Linked List

Basic Operations on Circular Linked Lists

Circular linked lists support a range of operations, each tailored to maintain the loop:

Insertion

  • At the beginning: Add a new node as the head, redirecting the last node’s pointer to it.
Insertion
  • At the end: Attach a new node after the last one, linking it back to the head.
At the end: Attach a new node after the last one, linking it back to the head.
  • After a specific node: Insert a node following a given position, adjusting pointers
After a specific node: Insert a node following a given position, adjusting pointers

Deletion

  • First node: Remove the head, updating the last node to point to the new head.
  • Last node: Delete the tail, rerouting the second-to-last node to the head.
  • Specific node: Excise a chosen node, reconnecting its neighbors.
				
					# Deletion of the first node
    def deleteFirst(self):
        if not self.head:
            return
        if self.head.next == self.head:
            self.head = None
        else:
            current = self.head
            while current.next != self.head:
                current = current.next
            current.next = self.head.next
            self.head = self.head.next


    # Deletion of the last node
    def deleteLast(self):
        if not self.head:
            return
        if self.head.next == self.head:
            self.head = None
        else:
            current = self.head
            while current.next.next != self.head:
                current = current.next
            current.next = self.head


    # Deletion by value (first occurrence)
    def deleteByValue(self, value):
        if not self.head:
            return
        current = self.head
        prev = None
        found = False
        while True:
            if current.data == value:
                found = True
                break
            prev = current
            current = current.next
            if current == self.head:
                break
        if found:
            if prev is None:  # It's the head
                self.deleteFirst()
            else:
                prev.next = current.next
        else:
            print("Value not found.")

				
			
				
					stack = [10, 20]
top_element = stack[-1]  # Get the top element (20)
print(top_element)  # Output: 20

				
			

Traversal

Start at any node and cycle through the list, returning to the starting point—a key feature for repetitive tasks.

				
					# Traversal and display
    def displayList(self):
        if not self.head:
            print("List is empty.")
            return
        current = self.head
        print(current.data, end=" -> ")
        current = current.next
        while current != self.head:
            print(current.data, end=" -> ")
            current = current.next
        print("(head)")


# Example usage
if __name__ == "__main__":
    cll = CircularLinkedList()
    cll.insertAtBeginning(10)
    cll.insertAtBeginning(20)
    cll.insertAtEnd(30)
    cll.insertAtEnd(40)
    cll.displayList()  # Output: 20 -> 10 -> 30 -> 40 -> (head)


    cll.deleteFirst()
    cll.displayList()  # Output: 10 -> 30 -> 40 -> (head)


    cll.deleteLast()
    cll.displayList()  # Output: 10 -> 30 -> (head)


    cll.deleteByValue(30)
    cll.displayList()  # Output: 10 -> (head)

				
			

Searching

Traverse the loop to find a target value, requiring safeguards against infinite loops.

For a quick refresher on these operations, try our crash course.

Implementation of Circular Linked Lists

Let’s see how circular linked lists come to life in code. Below are examples in C, C++, and Java for inserting nodes at the beginning and end.

C Implementation

				
					#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node* next;
};

struct Node* head = NULL;

void insertAtBeginning(int data) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = data;
    if (head == NULL) {
        newNode->next = newNode;
        head = newNode;
    } else {
        struct Node* temp = head;
        while (temp->next != head) temp = temp->next;
        temp->next = newNode;
        newNode->next = head;
        head = newNode;
    }
}

void insertAtEnd(int data) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = data;
    if (head == NULL) {
        newNode->next = newNode;
        head = newNode;
    } else {
        struct Node* temp = head;
        while (temp->next != head) temp = temp->next;
        temp->next = newNode;
        newNode->next = head;
    }
}

void displayList() {
    if (head == NULL) {
        printf("List is empty.\n");
        return;
    }
    struct Node* temp = head;
    do {
        printf("%d -> ", temp->data);
        temp = temp->next;
    } while (temp != head);
    printf("(head)\n");
}

int main() {
    insertAtBeginning(10);
    insertAtBeginning(20);
    insertAtEnd(30);
    insertAtEnd(40);
    displayList();
    return 0;
}

				
			
				
					C++ Implementation

#include <iostream>
using namespace std;

class CircularLinkedList {
public:
    struct Node {
        int data;
        Node* next;
    };
    Node* head;

    CircularLinkedList() { head = nullptr; }

    void insertAtBeginning(int data) {
        Node* newNode = new Node();
        newNode->data = data;
        if (!head) {
            newNode->next = newNode;
            head = newNode;
        } else {
            Node* temp = head;
            while (temp->next != head) temp = temp->next;
            temp->next = newNode;
            newNode->next = head;
            head = newNode;
        }
    }

    void insertAtEnd(int data) {
        Node* newNode = new Node();
        newNode->data = data;
        if (!head) {
            newNode->next = newNode;
            head = newNode;
        } else {
            Node* temp = head;
            while (temp->next != head) temp = temp->next;
            temp->next = newNode;
            newNode->next = head;
        }
    }

    void displayList() {
        if (!head) {
            cout << "List is empty.\n";
            return;
        }
        Node* temp = head;
        do {
            cout << temp->data << " -> ";
            temp = temp->next;
        } while (temp != head);
        cout << "(head)" << endl;
    }
};

int main() {
    CircularLinkedList cll;
    cll.insertAtBeginning(10);
    cll.insertAtBeginning(20);
    cll.insertAtEnd(30);
    cll.insertAtEnd(40);
    cll.displayList();
    return 0;
}


				
			
				
					class CircularLinkedList {
    static class Node {
        int data;
        Node next;
        Node(int data) {
            this.data = data;
            this.next = null;
        }
    }

    private Node head = null;

    public void insertAtBeginning(int data) {
        Node newNode = new Node(data);
        if (head == null) {
            newNode.next = newNode;
            head = newNode;
        } else {
            Node temp = head;
            while (temp.next != head) temp = temp.next;
            temp.next = newNode;
            newNode.next = head;
            head = newNode;
        }
    }

    public void insertAtEnd(int data) {
        Node newNode = new Node(data);
        if (head == null) {
            newNode.next = newNode;
            head = newNode;
        } else {
            Node temp = head;
            while (temp.next != head) temp = temp.next;
            temp.next = newNode;
            newNode.next = head;
        }
    }

    public void displayList() {
        if (head == null) {
            System.out.println("List is empty.");
            return;
        }
        Node temp = head;
        do {
            System.out.print(temp.data + " -> ");
            temp = temp.next;
        } while (temp != head);
        System.out.println("(head)");
    }

    public static void main(String[] args) {
        CircularLinkedList cll = new CircularLinkedList();
        cll.insertAtBeginning(10);
        cll.insertAtBeginning(20);
        cll.insertAtEnd(30);
        cll.insertAtEnd(40);
        cll.displayList();
    }
}

				
			
				
					class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

class CircularLinkedList:
    def __init__(self):
        self.head = None

    def insertAtBeginning(self, data):
        new_node = Node(data)
        if self.head is None:
            new_node.next = new_node
            self.head = new_node
        else:
            current = self.head
            while current.next != self.head:
                current = current.next
            current.next = new_node
            new_node.next = self.head
            self.head = new_node

    def insertAtEnd(self, data):
        new_node = Node(data)
        if self.head is None:
            new_node.next = new_node
            self.head = new_node
        else:
            current = self.head
            while current.next != self.head:
                current = current.next
            current.next = new_node
            new_node.next = self.head

    def displayList(self):
        if self.head is None:
            print("List is empty.")
            return
        current = self.head
        print(current.data, end=" -> ")
        current = current.next
        while current != self.head:
            print(current.data, end=" -> ")
            current = current.next
        print("(head)")

# Example usage
if __name__ == "__main__":
    cll = CircularLinkedList()
    cll.insertAtBeginning(10)
    cll.insertAtBeginning(20)
    cll.insertAtEnd(30)
    cll.insertAtEnd(40)
    cll.displayList()  # Output: 20 -> 10 -> 30 -> 40 -> (head)


				
			
				
					class Node {
    constructor(data) {
        this.data = data;
        this.next = null;
    }
}

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

    insertAtBeginning(data) {
        const newNode = new Node(data);
        if (!this.head) {
            newNode.next = newNode;
            this.head = newNode;
        } else {
            let current = this.head;
            while (current.next !== this.head) {
                current = current.next;
            }
            current.next = newNode;
            newNode.next = this.head;
            this.head = newNode;
        }
    }

    insertAtEnd(data) {
        const newNode = new Node(data);
        if (!this.head) {
            newNode.next = newNode;
            this.head = newNode;
        } else {
            let current = this.head;
            while (current.next !== this.head) {
                current = current.next;
            }
            current.next = newNode;
            newNode.next = this.head;
        }
    }

    displayList() {
        if (!this.head) {
            console.log("List is empty.");
            return;
        }
        let current = this.head;
        let output = "";
        do {
            output += current.data + " -> ";
            current = current.next;
        } while (current !== this.head);
        output += "(head)";
        console.log(output);
    }
}

// Example usage
const cll = new CircularLinkedList();
cll.insertAtBeginning(10);
cll.insertAtBeginning(20);
cll.insertAtEnd(30);
cll.insertAtEnd(40);
cll.displayList();  // Output: 20 -> 10 -> 30 -> 40 -> (head)

				
			

How is a stack implemented in programming?

Stacks can be implemented using arrays or linked lists. Array-based stacks have a fixed size, while linked list-based stacks can grow dynamically. For hands-on practice, our DSA course provides step-by-step coding exercises.

  • Stack Overflow: Occurs when trying to push an element onto a full stack.
  • Stack Underflow: Occurs when trying to pop an element from an empty stack.

Master error handling techniques with our web development course.

Yes, but it requires two queues to simulate the LIFO behavior of a stack. This advanced technique is explored in our master DSA and system design course.

  • Stack: Follows LIFO (Last In, First Out).
  • Queue: Follows FIFO (First In, First Out).

Dive into this comparison and more in our master DSA and web dev course.

Yes, stacks only allow access to the top element, which can be restrictive for certain applications. They also risk overflow and underflow if not managed properly. Learn to navigate these trade-offs in our data science course.

Preparing for a big interview? Check out our curated list of top 20 DSA interview questions or explore company-specific guides like Netflix, Meta, Amazon, or Atlassian to get a competitive edge.

DSA, High & Low Level System Designs

Buy for 60% OFF
₹25,000.00 ₹9,999.00

Accelerate your Path to a Product based Career

Boost your career or get hired at top product-based companies by joining our expertly crafted courses. Gain practical skills and real-world knowledge to help you succeed.

Reach Out Now

If you have any queries, please fill out this form. We will surely reach out to you.

Contact Email

Reach us at the following email address.

arun@getsdeready.com

Phone Number

You can reach us by phone as well.

+91-97737 28034

Our Location

Rohini, Sector-3, Delhi-110085

WhatsApp Icon

Master Your Interviews with Our Free Roadmap!

Hi Instagram Fam!
Get a FREE Cheat Sheet on System Design.

Hi LinkedIn Fam!
Get a FREE Cheat Sheet on System Design

Loved Our YouTube Videos? Get a FREE Cheat Sheet on System Design.