The problem. Given the head of a singly linked list, return true if it is circular — its last node points back to the head instead of to null — and false otherwise. An empty list is not circular. (A list whose last node points back into the middle is a different problem: Linked List Cycle.)
1 -> 2 -> 3 -> 4 -> (back to 1) -> true
1 -> 2 -> 3 -> 4 -> null -> false
5 -> (back to 5) -> true
empty list -> falseA circular list has no null to stop at. Every algorithm on one needs a different stopping rule — usually "we're back where we started" — and that's where most circular-list bugs come from: a loop written for a normal list runs forever. The questions in this chapter all turn on that idea: detecting the ring, using a ring on purpose (Rotate List closes one and cuts it), keeping a ring sorted, and simulating turn-taking round a circle.
Here the stopping rule decides the answer. Walk from the head: if you reach null, the list has an end; if you come back to the head, it's circular. With the guarantee that any loop goes back to the head, one of the two must happen within n steps.
Without trusting any guarantee, keep a set of visited nodes. The first node you meet twice tells you where the loop goes; the list is circular only if that node is the head.
function isCircular(head) { const seen = new Set(); for (let curr = head; curr !== null; curr = curr.next) { if (seen.has(curr)) return curr === head; // the first node seen twice decides it seen.add(curr); } return false; // reached null: the list has an end}Time O(n), space O(n). It even handles a loop back into the middle (answering false), but it pays for a set as big as the list.
Two ways out of the loop: null (not circular) or the head (circular).
function isCircular(head) { if (head === null) return false; // an empty list has no ring let curr = head.next; while (curr !== null && curr !== head) curr = curr.next; // until the end, or back at the start return curr === head; // back at the head: circular; fell off at null: not}Time O(n), space O(1). Starting at head.next matters: starting at the head itself, the loop would stop before taking a single step.
If the loop could go back to any node, approach 2 would run forever on a list like 1 → 2 → 3 → 2. Then use Floyd's slow and fast pointers (Linked List Cycle) to find whether there's a loop at all, in O(1) space.
head.next is the head.while (curr !== head) stops immediately; start at head.next.