Lesson 76 of 250
141. Linked List Cycle Detection
NeetCode: Linked List Cycle Detection · LeetCode: 141. Linked List Cycle Detection · Difficulty: Easy
Return true if a linked list contains a cycle, meaning some node's next points back to an earlier node.
Example
0 -> 1 -> 2 -> 3 -> back to 1 Output: true
0 -> 1 -> 2 -> 3 -> end Output: falseJava linked structures use null and mutable fields, so the list is a chain of ListNode objects and the end of the list is next == null. LeetCode builds a cycle by pointing the last node's next at the node at index pos. The helper build(vals, pos) does the same: it links the values in order and, when pos >= 0, points the last node back to node pos.
Brute force: remember every visited node
Walk the list and record each node in a HashSet. ListNode does not override equals or hashCode, so the set compares nodes by identity, which is what the problem needs. Seeing a node a second time means a cycle.
import java.util.HashSet;
import java.util.Set;
class ListNode {
int val;
ListNode next;
ListNode(int val) {
this.val = val;
}
}
class Solution {
public boolean hasCycle(ListNode head) {
Set<ListNode> seen = new HashSet<>();
ListNode cur = head;
while (cur != null) {
if (!seen.add(cur)) {
return true;
}
cur = cur.next;
}
return false;
}
}
public class Main {
static ListNode build(int[] vals, int pos) {
ListNode dummy = new ListNode(0);
ListNode tail = dummy;
ListNode target = null;
for (int i = 0; i < vals.length; i++) {
tail.next = new ListNode(vals[i]);
tail = tail.next;
if (i == pos) {
target = tail;
}
}
if (pos >= 0) {
tail.next = target;
}
return dummy.next;
}
public static void main(String[] args) {
Solution s = new Solution();
System.out.println(s.hasCycle(build(new int[]{0, 1, 2, 3}, 1)));
System.out.println(s.hasCycle(build(new int[]{0, 1, 2, 3}, -1)));
}
}true
falseTime: O(n). Space: O(n).
Optimal: Floyd's tortoise and hare
Move a slow pointer one step and a fast pointer two steps at a time. Without a cycle, fast reaches null. With a cycle, both end up circling inside it, and because fast gains one step on slow every round, it eventually lands exactly on slow.
class ListNode {
int val;
ListNode next;
ListNode(int val) {
this.val = val;
}
}
class Solution {
public boolean hasCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
return true;
}
}
return false;
}
}
public class Main {
static ListNode build(int[] vals, int pos) {
ListNode dummy = new ListNode(0);
ListNode tail = dummy;
ListNode target = null;
for (int i = 0; i < vals.length; i++) {
tail.next = new ListNode(vals[i]);
tail = tail.next;
if (i == pos) {
target = tail;
}
}
if (pos >= 0) {
tail.next = target;
}
return dummy.next;
}
public static void main(String[] args) {
Solution s = new Solution();
System.out.println(s.hasCycle(build(new int[]{0, 1, 2, 3}, 1)));
System.out.println(s.hasCycle(build(new int[]{0, 1, 2, 3}, -1)));
System.out.println(s.hasCycle(build(new int[]{}, -1)));
}
}true
false
falseThe loop checks fast.next != null before reading fast.next.next, so an empty list, a one-node list, and an even-length list all stop without a NullPointerException.
Time: O(n). Space: O(1).
