Linked lists are one of the most fundamental data structures, and detecting cycles is a classic interview problem. In this post, we’ll solve LeetCode 141 - Linked List Cycle using an elegant two-pointer technique known as Floyd’s Tortoise and Hare Algorithm.
Problem Statement
Given the head of a singly linked list, determine whether the linked list contains a cycle.
A cycle exists if, by following the next pointers, you can visit the same node more than once.
Example 1
Input:
1 → 2 → 3 → 4
↑ ↓
└───────┘
Output: trueThe tail connects back to the second node, creating a loop.
Example 2
Input:
1 → 2 → null
Output: falseThe list ends normally, so there is no cycle.
Intuition
The brute-force idea would be to keep track of every visited node using a set.
If we ever encounter a node we’ve already visited, we’ve found a cycle.
While this approach works, it requires O(n) extra space.
Can we solve it using constant space?
Yes!
Floyd’s Tortoise and Hare Algorithm
The key idea is to use two pointers moving at different speeds.
- Slow pointer moves one step at a time.
- Fast pointer moves two steps at a time.
Why does this work?
There are two possible scenarios:
Case 1: No Cycle
The fast pointer eventually reaches the end of the linked list (None).
1 → 2 → 3 → 4 → null
Slow: moves one node at a time
Fast: moves two nodes at a time
Fast reaches null → No cycleCase 2: Cycle Exists
Once both pointers enter the cycle, the fast pointer gains one node on the slow pointer during each iteration.
Eventually, the fast pointer catches up with the slow pointer.
┌──────────┐
↓ │
1 → 2 → 3 → 4 → 5
↑ │
└──────────┘
Slow: 1 step
Fast: 2 steps
Eventually:
Slow == FastIf the two pointers ever point to the same node, we know there’s a cycle.
Solution
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
slow, fast = head, head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return FalseStep-by-Step Dry Run
Consider the following linked list:
1 → 2 → 3 → 4
↑ ↓
└───────┘Initially:
Slow = 1
Fast = 1Iteration 1
Slow → 2
Fast → 3Iteration 2
Slow → 3
Fast → 2Iteration 3
Slow → 4
Fast → 4Both pointers meet!
Return:
TrueWhy Does the Fast Pointer Always Catch the Slow Pointer?
Think of two runners on a circular track.
- One runner moves one step at a time.
- The other moves two steps at a time.
Since they’re running on the same loop, the faster runner keeps gaining ground on the slower one. Eventually, it completes the gap and catches up.
The same logic applies inside a linked list cycle.
Complexity Analysis
Operation | Complexity |
|---|---|
Time | O(n) |
Space | O(1) |
- Each pointer traverses the list at most once.
- No additional data structures are used.
This makes Floyd’s algorithm both time-efficient and space-efficient.
Key Takeaways
- Use two pointers moving at different speeds.
- If the fast pointer reaches
None, there is no cycle. - If the slow and fast pointers meet, a cycle exists.
- Floyd’s Tortoise and Hare algorithm solves the problem in O(n) time and O(1) space, making it the optimal solution.
This is one of the most frequently asked linked list interview problems, and understanding the intuition behind the two-pointer approach will help you tackle many other linked list questions as well.