dsa3 min read

Linked List Data Structure: Singly, Doubly, and Circular Lists (2026)

Linked List Data Structure: Singly, Doubly, and Circular Lists (2026)

Published:  |  Category: Dsa  |  Reading time: ~15 min
Linked List Data Structure: Singly, Doubly, and Circular Lists (2026)

A linked list is a linear data structure where elements — called nodes — are not stored in contiguous memory. Each node contains data and a pointer (or reference) to the next node. This structure allows efficient insertions and deletions at arbitrary positions without shifting elements.

This guide covers singly linked lists, doubly linked lists, and circular linked lists. You will learn insertion, deletion, reversal, and traversal operations with complete code examples in C++, Java, and Python.

Singly Linked List Operations

A singly linked list has nodes with a data field and a next pointer. Insertion at the head is O(1); insertion at the tail requires traversal to the last node, making it O(n). Deletion follows a similar pattern — you must locate the predecessor node and update its next pointer.

Reversal is performed iteratively by maintaining three pointers: prev, curr, and next. Each iteration reverses the direction of the next pointer.

// C++
struct Node { int data; Node* next; };
void insertAtHead(Node*& head, int val) {
  Node* n = new Node{val, head};
  head = n;
}

// Java
class Node { int data; Node next; }
Node insertAtHead(Node head, int val) {
  return new Node(val, head);
}

// Python
class Node:
    def __init__(self, data):
        self.data = data
        self.next = None
def insert_at_head(head, val):
    n = Node(val)
    n.next = head
    return n

Doubly Linked List

A doubly linked list node contains prev and next pointers, enabling bidirectional traversal. Insertion and deletion are more flexible because you can reach the predecessor without traversing from the head. However, each node consumes extra memory for the additional pointer.

Reversal of a doubly linked list simply swaps every node's prev and next pointers, then updates the head to the last node.

// C++
struct Node { int data; Node *prev, *next; };
void insertEnd(Node*& head, int val) {
  Node* n = new Node{val, nullptr, nullptr};
  if(!head) { head = n; return; }
  Node* t = head;
  while(t->next) t = t->next;
  t->next = n; n->prev = t;
}

// Java
class Node { int data; Node prev, next; }

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

Circular Linked List

In a circular linked list, the last node points back to the head, forming a ring. This structure is useful for applications like round-robin scheduling. Traversal continues until you return to the starting node, so you must track a stopping condition carefully to avoid infinite loops.

Insertion and deletion follow similar logic to singly linked lists, but the tail's next pointer always references the head. For a circular doubly linked list, the head's prev points to the tail.

// C++
void insertCircular(Node*& head, int val) {
  Node* n = new Node{val};
  if(!head) { head = n; n->next = head; return; }
  Node* t = head;
  while(t->next != head) t = t->next;
  t->next = n; n->next = head;
}

// Java
// Similar logic with while(temp.next != head)

// Python
def insert_circular(head, val):
    n = Node(val)
    if not head:
        head = n; head.next = head; return
    t = head
    while t.next != head: t = t.next
    t.next = n; n.next = head

Frequently Asked Questions

What is the space complexity of a linked list?

O(n) for the data, plus O(n) for the pointers — each node stores 1 or 2 extra references.

When should I use a linked list over an array?

Use linked lists when you need frequent insertions/deletions from arbitrary positions. Use arrays when random access performance is critical.

Originally published on Ayodhyyya. Last updated June 1, 2026.