Remove a Loop in a Linked List
Solve this Problemhead of a singly linked list that may contain a cycle, remove the cycle — without deleting any node — and return the head of the resulting (now normal, terminating) list.
This builds directly on Floyd's Cycle Detection: once slow and fast meet inside the cycle, the same distance property that locates the loop's first node can instead locate its last node — the one whose next pointer is the cyclic edge itself. Setting that single pointer to null is enough; no node is copied, moved, or deleted.
Test Case 1:
Test Case 2:
Test Case 3:
Constraints
- ◆
0 ≤ number of nodes in head ≤ 10⁴ - ◆
-10⁹ ≤ node value ≤ 10⁹ - ◆
A cyclic test list is described as (vals, pos) — the last node's next points back to index pos (0-indexed); pos = -1 means no cycle - ◆
No node is deleted — only the cyclic edge is unlinked, so the returned list has the same values in the same order
Try the Dry Run
Don't just read the solution — watch it execute, one step at a time.
| 1 | class Solution { |
| 2 | public Node removeLoop(Node head) { |
| 3 | if (head == null) { |
| 4 | return head; |
| 5 | } |
| 6 | Node slow = head, fast = head; |
| 7 | boolean hasLoop = false; |
| 8 | while (fast != null && fast.next != null) { |
| 9 | slow = slow.next; |
| 10 | fast = fast.next.next; |
| 11 | if (slow == fast) { |
| 12 | hasLoop = true; |
| 13 | break; |
| 14 | } |
| 15 | } |
| 16 | if (!hasLoop) { |
| 17 | return head; |
| 18 | } |
| 19 | if (slow == head) { |
| 20 | while (fast.next != slow) { |
| 21 | fast = fast.next; |
| 22 | } |
| 23 | } else { |
| 24 | slow = head; |
| 25 | while (slow.next != fast.next) { |
| 26 | slow = slow.next; |
| 27 | fast = fast.next; |
| 28 | } |
| 29 | } |
| 30 | fast.next = null; |
| 31 | return head; |
| 32 | } |
| 33 | } |
| 34 |
1 (node 0)head is not null, so skip the empty-list base case.
Approach & Solutions
Brute Force — Hash Set to Find the Node Whose Next Repeats, Then Snip
GoodWalk the list while tracking prev (the node right before curr). Add each node to a hash set as it's visited. The moment curr is a node that's already in the set, prev must be the true last node of the cycle — the one whose next pointer creates the loop — so set prev.next to null. Correct, but storing every visited node costs O(n) extra space.
O(n)O(n)1// Node definition used in this problem:
2// class Node {
3// int val;
4// Node next;
5// }
6
7class Solution {
8 public Node removeLoop(Node head) {
9 Set<Node> visited = new HashSet<>();
10 Node curr = head;
11 Node prev = null;
12 while (curr != null) {
13 if (visited.contains(curr)) {
14 prev.next = null;
15 return head;
16 }
17 visited.add(curr);
18 prev = curr;
19 curr = curr.next;
20 }
21 return head;
22 }
23}Optimal — Floyd's Algorithm to Locate and Snip the Loop
OptimalPhase 1: run slow/fast pointers until they meet inside the cycle (or fast reaches the end — no loop). Phase 2: if the meeting point is head itself, walk one pointer all the way around the cycle until its next is the meeting point. Otherwise, reset slow to head and advance both pointers one step at a time until slow.next equals fast.next — at that moment fast is sitting on the true last node of the cycle. Either way, set that node's next to null.
O(n)O(1)1// Node definition used in this problem:
2// class Node {
3// int val;
4// Node next;
5// }
6
7class Solution {
8 public Node removeLoop(Node head) {
9 if (head == null) {
10 return head;
11 }
12 Node slow = head, fast = head;
13 boolean hasLoop = false;
14 while (fast != null && fast.next != null) {
15 slow = slow.next;
16 fast = fast.next.next;
17 if (slow == fast) {
18 hasLoop = true;
19 break;
20 }
21 }
22 if (!hasLoop) {
23 return head;
24 }
25 if (slow == head) {
26 while (fast.next != slow) {
27 fast = fast.next;
28 }
29 } else {
30 slow = head;
31 while (slow.next != fast.next) {
32 slow = slow.next;
33 fast = fast.next;
34 }
35 }
36 fast.next = null;
37 return head;
38 }
39}