Table of Contents
Problem Description
Subproblem 1: Determine Whether a Linked List Contains a Cycle
Given the head node head of a singly linked list, determine whether a cycle exists in the linked list. If there is a node in the linked list that can be reached again by continuously following the next pointer, then a cycle exists in the linked list.
Input: head = 3→2→0→-4 ↑_______↓Output: trueSubproblem 2: Find the Starting Point of the Cycle
Building on the previous problem, if a cycle exists, return the entry node of the cycle.
Input: head = 3→2→0→-4 ↑_______↓Output: node 2Explanation: the entry of the cycle is node 2Solution Approach
Phase 1: Floyd’s Cycle Detection (Determine Whether a Cycle Exists)
Use fast and slow pointers: slow moves one step at a time, fast moves two steps at a time.
- If the linked list has no cycle: fast will reach
nullfirst, and we determine there is no cycle - If the linked list has a cycle: fast and slow will definitely meet in the cycle (just like on a circular track, the faster person will always lap the slower person)
Phase 2: Find the Entry of the Cycle (Mathematical Derivation)
After fast and slow meet in the cycle:
- Point slow back to the head of the linked list
- slow and fast advance at the same speed (one step at a time)
- The node where they meet again is the entry of the cycle
Mathematical Proof
Let:
- The distance from the head of the linked list to the cycle entry be a
- The distance from the cycle entry to the first meeting point be b
- The length of the cycle be L, then the remaining distance from the meeting point to the cycle entry is c = L - b
At the first meeting:
- slow has walked
a + bsteps - fast has walked
a + b + n·Lsteps (n is the number of extra laps fast has made)
Since fast’s speed is twice that of slow: 2(a + b) = a + b + n·L
→ a + b = n·L
→ a = n·L - b = (n-1)·L + (L - b) = (n-1)·L + c
a = c + (n-1)·L — This means that the distance of a steps from the head of the linked list to the cycle entry equals the distance of c steps from the meeting point to the cycle entry (possibly with a few extra laps).
Therefore, if we let slow start from the head and fast start from the meeting point, and both advance at the same speed, they will definitely meet at the cycle entry.
Code Implementation
#include <stdio.h>#include <stdlib.h>#include <stdbool.h>
typedef struct ListNode { int val; struct ListNode *next;} ListNode;
/* Check whether a cycle exists */bool hasCycle(ListNode *head) { ListNode *slow = head; ListNode *fast = head;
while (fast && fast->next) { slow = slow->next; fast = fast->next->next; if (slow == fast) return true; // Fast and slow pointers meet, so a cycle exists } return false; // fast reaches the end, so there is no cycle}
/* Find the entry node of the cycle */ListNode* detectCycle(ListNode *head) { ListNode *slow = head; ListNode *fast = head;
// Phase 1: Determine whether a cycle exists while (fast && fast->next) { slow = slow->next; fast = fast->next->next; if (slow == fast) { // Phase 2: Find the entry point of the cycle slow = head; while (slow != fast) { slow = slow->next; fast = fast->next; } return slow; // The meeting point again is the cycle entry } } return NULL; // No cycle}struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(nullptr) {}};
class Solution {public: // Check whether a cycle exists bool hasCycle(ListNode *head) { ListNode *slow = head; ListNode *fast = head;
while (fast && fast->next) { slow = slow->next; fast = fast->next->next; if (slow == fast) return true; } return false; }
// Find the entry node of the cycle ListNode* detectCycle(ListNode *head) { ListNode *slow = head; ListNode *fast = head;
// Phase 1: Determine whether a cycle exists while (fast && fast->next) { slow = slow->next; fast = fast->next->next; if (slow == fast) { // Phase 2: Find the entry point of the cycle slow = head; while (slow != fast) { slow = slow->next; fast = fast->next; } return slow; } } return nullptr; }};class ListNode { constructor(val) { this.val = val; this.next = null; }}
// Check whether a cycle existsfunction hasCycle(head) { let slow = head; let fast = head;
while (fast && fast.next) { slow = slow.next; fast = fast.next.next; if (slow === fast) return true; } return false;}
// Find the entry node of the cyclefunction detectCycle(head) { let slow = head; let fast = head;
// Phase 1: Determine whether a cycle exists while (fast && fast.next) { slow = slow.next; fast = fast.next.next; if (slow === fast) { // Phase 2: Find the entry point of the cycle slow = head; while (slow !== fast) { slow = slow.next; fast = fast.next; } return slow; } } return null;}Illustrated Process
Phase 1: Cycle Detection
Linked list: 3 → 2 → 0 → -4 ↑__________↓
Step 1: slow=3, fast=3Step 2: slow=2, fast=0Step 3: slow=0, fast=2 ← fast has gone all the way around and lapped slowStep 4: slow=-4, fast=-4 ← they meet at node -4! There is a cycle!Phase 2: Find the Cycle Entry
After the first meeting: slow resets to the head (3) fast stays at the meeting point (-4)
Advance at the same speed:Step 1: slow=2, fast=2 ↓ ↓ The two meet at node 2 → node 2 is the entrance to the cycle!Complexity Analysis
| Dimension | Analysis |
|---|---|
| Time Complexity | O(n) — slow walks at most n steps (from head to entry and then to the meeting point), each phase is O(n) |
| Space Complexity | O(1) — only two pointers are used |
Key Takeaways
-
The core of Floyd’s cycle detection is the speed difference: As long as fast is faster than slow (not necessarily 2x), and the speed difference is coprime with the cycle length, the cycle can be detected. Choosing 2x is the engineering-optimal practice.
-
The mathematical proof is an interview highlight: Being able to derive
a = c + (n-1)·Lis the key to distinguishing “memorizing templates” from “truly understanding.” -
The elegance of the equal-speed return search: In the second phase, slow and fast walk at the same speed, and their meeting point is the entry — this is the most beautiful part of Floyd’s algorithm.
Extended Thinking
- If you need to calculate the length of the cycle, how would you do it? (After meeting, keep fast stationary, and let slow continue walking one full lap while counting)
- Can the fast pointer move 3 steps at a time? What impact would that have?
Related Articles:
- Finding the Middle of a Linked List — A simple application of fast and slow pointers
- Determine Intersection and Find the Intersection Point — Another two-pointer classic