Quick Overview
Two pointers moving at different speeds must eventually meet inside a loop, the way a faster runner laps a slower one. That detects a cycle in `O(N)` time and `O(1)` space, with no visited set.
The Fast and Slow Pointer Pattern (also known as Floyd's Cycle-Finding Algorithm) uses two pointers that traverse a sequence at different speeds:
slow): Advances 1 step at a time.fast): Advances 2 steps at a time.If a cycle exists in a linked list or sequence, the fast pointer will eventually loop around and lap the slow pointer, meeting at the exact same node in O(N) time and O(1) space without storing visited nodes in a hash set!
class ListNode {
constructor(val, next = null) {
this.val = val;
this.next = next;
}
}
function hasCycle(head) {
if (!head || !head.next) return false;
let slow = head;
let fast = head;
while (fast !== null && fast.next !== null) {
slow = slow.next; // Move 1 step
fast = fast.next.next; // Move 2 steps
if (slow === fast) {
return true; // Fast pointer caught up to slow pointer (Cycle detected!)
}
}
return false; // Fast pointer reached end of list (No cycle)
}
When the fast pointer reaches the end of the list, the slow pointer is guaranteed to be at the exact middle node:
function findMiddleNode(head) {
let slow = head;
let fast = head;
while (fast !== null && fast.next !== null) {
slow = slow.next;
fast = fast.next.next;
}
return slow; // Points to the middle element!
}
while (fast.next !== null) causes a crash when fast is null. Always check BOTH while (fast !== null && fast.next !== null).Use fast and slow pointers for list cycles and midpoints. Next, let's explore the Sliding Window Pattern!
Big O describes how work grows, not how long it takes. An `O(N^2)` algorithm on 1,000 items does a million steps; the same work at `O(N log N)` does about 10,000.
A dynamic array doubles rather than growing by one, so the expensive copy lands on the 1st, 2nd, 4th and 8th push. Averaged across every push, that is `O(1)` amortised.
A nested loop tests every pair in `O(N^2)`. On sorted data, moving two pointers inward from both ends rules out half the remaining candidates at each step and settles it in one `O(N)` pass.