Two pointer introduction

The two-pointer technique

Many array and string problems can be solved with two indices moving through the data, instead of nested loops. Replacing an O(n²) double loop with two pointers that each move O(n) times is one of the most common speed-ups you'll use.

Two flavours

Converging pointers start at opposite ends and move toward each other. Great for palindromes, reversing, and "find a pair in a sorted array":

left, right = 0, length(s) - 1
while left < right:
    // compare or use s[left] and s[right]
    left += 1
    right -= 1

Fast / slow (read / write) pointers both start at the left, moving at different rates. Great for filtering or compacting an array in place — one pointer reads, the other writes:

write = 0
for read in 0..length(nums)-1:
    if keep(nums[read]):
        nums[write] = nums[read]
        write += 1
// nums[0..write-1] now holds the kept elements

Why it works

Each pointer advances at most n times, so the whole scan is O(n) with O(1) extra space (for in-place variants). The skill is choosing which flavour fits and defining the rule for advancing each pointer. The problems in this section drill both: comparing ends (palindrome, subsequence) and read/write compaction (five sort, compress/uncompress).