Skip to content

Circular Linked List Visualizer

Circular + tailn = 7≈ 84 B
new = Node(99)
from head:12→7→33→5→21→48→↺ back to head
1/6

What is a circular linked list?

A circular linked list is a linked list whose last node points back to the first node instead of to NULL, so the nodes form a ring with no end. With a single pointer to the last node you reach both ends in O(1) — the head is just last.next — which makes it a natural fit for round-robin scheduling, turn-taking and anything else that cycles through items forever.

How it works

  1. Allocate the new node. If the list is empty, point the node at itself (new.next = new) and make it both head and tail — a ring of one.
  2. Otherwise aim the new node at the current head: new.next = head.
  3. Close the ring through it: tail.next = new.
  4. Insert at head: move head = new. Insert at tail: move tail = new instead — that one assignment is the only difference.
  5. To traverse, start at head, visit, follow next — and stop when you arrive back at head, not at NULL.

How a circular list differs from a singly linked list

The node is the same [data | next] as in a singly linked list; only the invariant changes: the last node's next is the head, not NULL. That has three consequences.

  • There is no NULL to stop at. A loop written as while curr is not None never ends. Traversal becomes "start at the head and stop when you are back at the head", which is naturally a do-while loop, because the start and the stop condition are the same node.
  • Every node has a successor. There is no "last node has no next" special case, and rotating the whole ring by one is O(1): head = head.next, tail = tail.next.
  • The ends must stay connected. Any operation that changes the head — insert at head, delete head — must also update last.next, or the ring breaks.

Why keep a pointer to the last node

If the list keeps only head, inserting at the head is surprisingly O(n): the new head must be pointed at by the last node, and finding the last node means walking the entire ring. Keep a pointer to the last node instead and both ends are one step away — the tail is last and the head is last.next. Many textbooks store only last for exactly this reason. The visualizer keeps head and tail side by side so you can see both; switch the tail pointer off and insert at head falls back to an O(n) walk.

Deleting the tail is still O(n) in a circular singly linked list, for the same reason as in an ordinary singly linked list: the new tail is the node before the old one, and only a walk finds it. A circular doubly linked list removes that cost too.

Worked example: insert 99 at the head

This page opens with the ring 12 → 7 → 33 → 5 → 21 → 48 → (back to 12), with head on 12 and tail on 48, and this insert ready to play:

  • new.next = head — 99 points at 12.
  • tail.next = new — 48 now points at 99 instead of 12, so the ring runs through the new node.
  • head = new — the ring reads 99 → 12 → 7 → 33 → 5 → 21 → 48 → (back to 99).

Three pointer writes and no walking: O(1). Had we inserted at the tail instead, the first two writes would be identical and the last line would be tail = new, giving 12 → … → 48 → 99 → (back to 12). In a ring, "head" and "tail" are just two neighbouring positions you have chosen to name.

Traversing, searching and deleting safely

Search starts at head and checks one node per step. Written as a do-while loop, it visits every node exactly once and stops as soon as curr returns to head, so an unsuccessful search costs exactly n comparisons, just like in a NULL-terminated list.

Deletion has two ring-specific edge cases. Deleting the head must also repoint last.next at the new head. Deleting the only node — the one whose next is itself — must set both head and tail to NULL; otherwise you are left holding a pointer to freed memory. The Delete buttons in the visualizer animate both cases.

Where circular linked lists are used

  • Round-robin scheduling: a CPU scheduler or load balancer serves the item at the head, then advances one step; in a ring nobody is ever "at the end".
  • Turn order in multiplayer games, and the classic Josephus problem, where people stand in a circle and every k-th one is removed.
  • Repeating playlists and slideshows that wrap from the last item back to the first.
  • Kernel lists: the Linux list_head is a circular doubly linked list with a sentinel node, so an empty list is a node that points at itself.

A fixed-size ring buffer applies the same idea to an array with two wrapping indices instead of pointers — see the circular queue on the stack and queue page.

Common mistakes

  • Writing while curr is not None and looping forever.
  • Forgetting last.next = new_head when inserting or deleting at the head.
  • Not handling the one-node ring, whose next points at itself.
  • Assuming a list is circular without checking. If you only suspect a loop, detect it with Floyd's tortoise-and-hare algorithm — on a fully circular list, the cycle it finds starts at the head.

Time and space complexity

CaseTimeSpaceWhy
Insert at headO(1)O(1)Needs the last node to close the ring; without a tail/last pointer that is an O(n) walk.
Insert at tailO(1)O(1)new.next = head; tail.next = new; tail = new.
Delete headO(1)O(1)Move head and repoint tail.next; O(n) without a tail pointer.
Delete tailO(n)O(1)The new tail is the old tail's predecessor, found only by walking (O(1) if doubly linked).
Search / traverseO(n)O(1)Visit each node once and stop when you are back at head.
Rotate by oneO(1)O(1)head = head.next; tail = tail.next — nothing else moves.

Circular Linked List in Python

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


class LinkedList:
    def __init__(self):
        self.head = None  # first node
        self.tail = None  # last node
        # invariant: last.next is self.head

    def insert_at_head(self, value):
        new_node = Node(value)
        if self.head is None:
            new_node.next = new_node  # ring of one
            self.head = self.tail = new_node
            return
        new_node.next = self.head
        last = self.tail  # O(1)
        last.next = new_node
        self.head = new_node

    def insert_at_tail(self, value):
        new_node = Node(value)
        if self.head is None:
            new_node.next = new_node
            self.head = self.tail = new_node
            return
        new_node.next = self.head  # keep ring
        self.tail.next = new_node  # O(1)
        self.tail = new_node

    def delete_head(self):
        if self.head is None:
            raise IndexError("delete from empty list")
        if self.head.next is self.head:  # 1 node
            self.head = self.tail = None
            return
        last = self.tail
        temp = self.head
        self.head = self.head.next
        last.next = self.head
        del temp  # free(temp): the GC reclaims it

    def search(self, value):
        if self.head is None:
            return -1
        curr, i = self.head, 0
        while True:  # do ... while curr is not head
            if curr.data == value:
                return i
            curr, i = curr.next, i + 1
            if curr is self.head:
                break
        return -1

The visualizer above shows the same code in Python, C++, Java and JavaScript.

Frequently asked questions

How do you know when to stop traversing a circular linked list?

Stop when the pointer comes back to the node you started from (usually head), not when it reaches NULL — there is no NULL. A do-while loop expresses this naturally, because the first node must be visited before the stop test runs.

What is the advantage of a circular linked list?

Every node has a successor, so you can start anywhere and still reach every node, rotate the list in O(1), and — with a pointer to the last node — insert at both the head and the tail in O(1).

Can a circular linked list be doubly linked?

Yes. In a circular doubly linked list the head's prev is the tail and the tail's next is the head, which makes insertion and deletion at both ends O(1). The Linux kernel's list_head is this kind of list.

How do you check whether a linked list is circular?

Walk from the head: if you return to the head before reaching NULL, the list is circular. To detect any loop, even one that does not pass through the head, use Floyd's cycle detection with slow and fast pointers.

What is the Josephus problem?

n people stand in a circle and every k-th person is eliminated until one remains. A circular linked list simulates it directly — advance k − 1 steps, unlink the next node, repeat — in O(n·k) time; a recurrence finds the survivor in O(n).

Keep learning

Last updated