Two Pointers
Walk two indices through a sequence to cut nested loops down to one pass
Free tier: read the explanation here. Upgrade to Pro for Drills, Speed challenges & Mastery badges.
UpgradeKnowledge Debt detected
You can study this freely — but your score may plateau if these foundations have gaps. The Mastery badge requires them to be solid.
Explanation
replaces an O(n²) brute-force scan with a single O(n) pass by moving two indices through the data instead of one.
The three shapes this pattern takes:
- 1Converging — start at both ends, move inward. Used for sorted-array sum/pair problems.
- 2Same direction (sliding window) — both pointers move right; the gap between them is the "window." Used for substring/subarray problems.
- 3Fast/slow — one pointer moves 2 steps for every 1 of the other. Used for cycle detection in linked lists.
# Converging — find a pair in a sorted array that sums to target
def two_sum_sorted(arr, target):
lo, hi = 0, len(arr) - 1
while lo < hi:
s = arr[lo] + arr[hi]
if s == target:
return (lo, hi)
elif s < target:
lo += 1 # sum too small — need a bigger number
else:
hi -= 1 # sum too big — need a smaller number
return NoneWhy it works: because the array is sorted, moving lo right can only increase the sum, and moving hi left can only decrease it. Every step eliminates exactly the pairs that can't possibly work — no pair is ever re-checked.
# Sliding window — longest substring with no repeated characters
def longest_unique_substring(s):
seen = set()
left = 0
best = 0
for right in range(len(s)):
while s[right] in seen: # shrink from the left until valid again
seen.remove(s[left])
left += 1
seen.add(s[right])
best = max(best, right - left + 1)
return bestThe tell: if a brute-force solution checks every pair (for i ... for j in range(i+1, n)) over a sorted array, a linked list, or a contiguous substring/subarray, two pointers usually turns it from O(n²) into O(n).
Examples
Removing duplicates in place (converging)
Two pointers in the same direction — one reads, one writes — dedupes a sorted array in O(n) with O(1) extra space
def remove_duplicates(arr):
if not arr:
return 0
write = 1 # next slot to write a unique value
for read in range(1, len(arr)):
if arr[read] != arr[write - 1]:
arr[write] = arr[read]
write += 1
return write # length of the deduplicated prefix
arr = [1, 1, 2, 2, 2, 3, 4, 4]
length = remove_duplicates(arr)
print(arr[:length]) # [1, 2, 3, 4]Fast/slow pointers — detecting a cycle
Floyd's cycle detection — if there's a loop, the fast pointer always laps the slow one
def has_cycle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next # moves 1 step
fast = fast.next.next # moves 2 steps
if slow is fast:
return True # they met — there's a cycle
return False # fast hit the end — no cycleHow well did you understand this?
Practice Problems
Next in Algorithms & Data Structures
Depth-First Search (DFS)