Linked List · 16 of 36
Check if a linked list is a circular linked list
Decide if a chain of boxes is a ring, where the last box holds hands with the very first box again.
- Simple way
- time O(n)
room O(n) - Clever way
- time O(n)
room O(1)
The problem, simply
You get the head of a linked list. Say true if the list is circular: the last box does not point to null, it points back to the head, so the boxes form a ring. Say false otherwise.
There are three kinds of lists you might get. A plain list that ends in null. A ring, where the tail points to the head. And a list with a loop somewhere in the middle, where the tail points back to some box that is not the head. Only the ring counts as circular.
A story to remember it
Picture it
A circular list. The tail, 5, points back to the head, 1. There is no null anywhere.
- answer
Not circular: the tail points back to 3, so the loop does not include the head.
- rejected
Examples
A full ring
- In
1 -> 2 -> 3 -> 4 -> 5 -> back to 1- Out
true
The tail points to the head, so the boxes make one ring.
A plain list
- In
1 -> 2 -> 3 -> null- Out
false
The walk falls off the end into null.
A loop in the middle
- In
1 -> 2 -> 3 -> 4 -> 5 -> back to 3- Out
false
There is a loop, but it starts at 3, not at the head. This is the case that trips people up.
One box pointing to itself
- In
7 -> back to 7- Out
true
A ring of one. The tail is the head.
Empty list
- In
null- Out
true or false
Ask. The GeeksforGeeks version says an empty list counts as circular, many others say false. Handle it before anything else.
Way 1: the simple way
Walk the list and keep a bag of every box you have stepped on, a hash set. Stop as soon as one of three things happens:
- You reach
null. It is a line, returnfalse. - You reach the head again. It is a ring, return
true. - You reach a box that is already in the bag, but it is not the head. There is a loop that skips the head, return
false.
Walking the sneaky list. The bag already holds 1 to 5. The next step lands on 3, which is in the bag and is not the head.
- rejected
class Solution {
public boolean isCircular(ListNode head) {
if (head == null) return true; // the GfG rule; ask your interviewer
Set<ListNode> seen = new HashSet<>();
ListNode curr = head;
while (curr != null) {
if (seen.contains(curr)) return curr == head; // back at the head means a ring
seen.add(curr);
curr = curr.next;
}
return false; // fell off the end
}
}How long it takes: each box is visited once before we stop, O(n). Extra room: the bag can hold every box, O(n).
Way 2: the clever way
Use two runners on the list, a slow one that takes one step and a fast one that takes two. If the list ends, fast finds null first and we say false. If there is any loop, fast laps slow and they land on the same box.
A loop is not enough, though. We must know where the loop begins. Here is the magic part: put slow back on the head, leave fast where they met, and move both one step at a time. They meet again exactly at the box where the loop begins. The list is circular only if that box is the head.
- Move slow by 1 and fast by 2 until fast hits
null(returnfalse) or they meet. - Put slow back on the head. Move both by 1 until they meet.
- Return
trueif they meet on the head.
A list whose tail comes back to the head
01 / 07
Both runners start at the head. slow takes one step at a time, fast takes two.
- looking now
- checking
- answer
- path
The same two runners on the sneaky list:
A loop that does not come back to the head
01 / 07
Both runners start at the head. slow takes one step at a time, fast takes two.
- looking now
- checking
- rejected
- path
class Solution {
public boolean isCircular(ListNode head) {
if (head == null) return true; // the GfG rule; ask your interviewer
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) { // there is a loop
slow = head;
while (slow != fast) { // walk both to where the loop begins
slow = slow.next;
fast = fast.next;
}
return slow == head; // circular only if it begins at the head
}
}
return false; // fast found the end
}
}How long it takes: fast catches slow within about two trips around the list, and the second phase walks at most n more steps, O(n). Extra room: two fingers, O(1).
Watch out for
- An empty list: decide the answer first. GeeksforGeeks says
true. - One box pointing to itself is circular. Both ways handle it: slow and fast meet on the head after one step.
- In the fast loop, check
fast != null && fast.next != nullbefore jumping two steps, or you get a null pointer crash on a plain list. - Circular is not the same as "has a loop". A loop is anywhere; circular means the loop starts at the head.
What interviewers ask next
- Why does the second phase find the loop's first box? Say the tail before the loop has
aboxes and slow walkeda + bsteps when they met. Fast walked twice that, so the extraa + bsteps are whole laps of the loop. That means walkingamore steps from the meeting point lands on the loop's first box, and so does walkingasteps from the head. - How long is the loop? After they meet, keep fast still and walk slow around until it comes back. Count the steps.
- Count the boxes of a circular list. Start at the head and walk with a do while loop until you are back at the head.
- Can you check it if you are allowed to change the list? Yes: flip arrows as you go, and you come back to the head if there is any loop. That is clever but wrecks the list, so it is rarely what they want.
Remember this
Grown-up words: the ring of kids is a circular linked list, a ring with a tail is a cycle that does not include the head, the bag of seen boxes is a hash set, and the two runners are Floyd's cycle detection, also called the tortoise and the hare.