← All Guides

Two Pointers

4 drills · 4 pattern exercises · 8 micro drills

Patterns Overview

Patterns used in this topic, with difficulty breakdown.

Greedy Shrink 1 drill 1 Med
Two Pointer Max 1 drill 1 Hard
Sort + Two Pointer 1 drill 1 Med
Slow-Fast Write 1 drill 1 Easy

Foundation Drill

126 Remove Duplicates from Sorted Array Easy Slow-Fast Write

Remove duplicate values from a sorted list in-place and return how many unique values remain.

Start here in Foundations →

Coding Drills

All 4 drills grouped by pattern. Each includes prompt, solution, and complexity.

Greedy Shrink (1)

123 Container With Most Water Medium

In plain English: Pick two vertical lines to form a container. Find the pair that holds the most water.

Start with the widest container (both ends). The shorter line is the bottleneck — moving the taller one inward can only reduce width without increasing height. So always move the shorter pointer inward.

Prompt

Given n non-negative integers representing heights of vertical lines, find two lines that together with the x-axis form a container that holds the most water.

Solution

def max_area(height):
    l, r = 0, len(height) - 1
    best = 0
    while l < r:
        w = r - l
        h = min(height[l], height[r])
        best = max(best, w * h)
        if height[l] < height[r]:
            l += 1   # move shorter line inward
        else:
            r -= 1
    return best

# Test: max_area([1,8,6,2,5,4,8,3,7]) == 49  (lines at indices 1,8)
O(n) time · O(1) space

Two Pointer Max (1)

124 Trapping Rain Water Hard

In plain English: Given a bar chart of heights, figure out how much rainwater gets trapped in the valleys between bars.

Water at any position = min(max_left, max_right) - height. Use two pointers: process from the side with the smaller max. That side's water level is determined since the other side is at least as tall.

Prompt

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining.

Solution

def trap(height):
    l, r = 0, len(height) - 1
    left_max = right_max = 0
    water = 0
    while l < r:
        # process from the shorter side: its water level is fully determined
        # because the opposite side is at least as tall
        if height[l] < height[r]:
            left_max = max(left_max, height[l])
            water += left_max - height[l]  # gap between max seen and current bar
            l += 1
        else:
            right_max = max(right_max, height[r])
            water += right_max - height[r]
            r -= 1
    return water

# Test: trap([0,1,0,2,1,0,1,3,2,1,2,1]) == 6
# Test: trap([4,2,0,3,2,5]) == 9
O(n) time · O(1) space

Sort + Two Pointer (1)

125 3Sum Medium

In plain English: Find all unique sets of three numbers in the array that add up to zero.

Sort the array, fix one element, then use two pointers on the remainder to find pairs summing to the negative of the fixed element. Skip duplicates at each level to avoid repeated triplets.

Prompt

Given an integer array nums, return all unique triplets [nums[i], nums[j], nums[k]] such that i != j != k and nums[i] + nums[j] + nums[k] == 0.

Solution

def three_sum(nums):
    nums.sort()
    result = []
    for i in range(len(nums) - 2):
        if i > 0 and nums[i] == nums[i-1]:
            continue  # skip duplicate fixed element
        l, r = i + 1, len(nums) - 1
        while l < r:
            total = nums[i] + nums[l] + nums[r]
            if total == 0:
                result.append([nums[i], nums[l], nums[r]])
                while l < r and nums[l] == nums[l+1]:
                    l += 1  # skip duplicates
                while l < r and nums[r] == nums[r-1]:
                    r -= 1  # skip duplicates
                l += 1; r -= 1
            elif total < 0:
                l += 1
            else:
                r -= 1
    return result

# Test: three_sum([-1,0,1,2,-1,-4]) == [[-1,-1,2],[-1,0,1]]
# Test: three_sum([0,0,0]) == [[0,0,0]]
O(n^2) time · O(1) space (excluding output)

Slow-Fast Write (1)

126 Remove Duplicates from Sorted Array Easy

In plain English: Remove duplicate values from a sorted list in-place and return how many unique values remain.

Slow pointer marks the write position, fast pointer scans ahead. When fast finds a new value, write it at slow+1 and advance slow. The sorted order guarantees duplicates are adjacent.

Prompt

Given a sorted array nums, remove the duplicates in-place such that each element appears only once. Return the new length.

Solution

def remove_duplicates(nums):
    if not nums:
        return 0
    slow = 0  # slow = next write position for a unique value
    for fast in range(1, len(nums)):  # fast scans ahead for new values
        if nums[fast] != nums[slow]:
            slow += 1
            nums[slow] = nums[fast]
    return slow + 1

# Test: nums = [1,1,2]; remove_duplicates(nums) == 2; nums[:2] == [1,2]
# Test: nums = [0,0,1,1,1,2,2,3,3,4]
#       remove_duplicates(nums) == 5; nums[:5] == [0,1,2,3,4]
O(n) time · O(1) space

Pattern Recognition

4 exercises: spot the signals, pick the method, avoid the trap.

123 Container With Most Water Medium

Given n vertical lines, find two that form a container holding the most water.

Signals

Two Pointers

Start at widest. Move the shorter line inward (can't do better keeping it). O(n).

Greedy

Same logic. Moving the taller line can only decrease width without guaranteeing height increase.

Trap

Don't try all O(n²) pairs. The greedy proof: moving the taller side can never improve area.

124 Trapping Rain Water Hard

Given n non-negative integers representing an elevation map, compute how much water it can trap.

Signals

Two Pointers

Two pointers from ends. Process the side with smaller max. Water = max - height. O(n) time, O(1) space.

Stack

Monotonic stack tracking bars. Pop when current > stack top, compute trapped water. O(n) time, O(n) space.

Trap

The prefix/suffix max arrays work but use O(n) space. Two pointers achieve O(1) space.

125 3Sum Medium

Given array nums, find all unique triplets that sum to zero.

Signals

Two Pointers

Sort. Fix i, two pointers on [i+1..n-1]. Skip duplicate values of i, l, r. O(n²).

Hash Map

For each pair, check if -(a+b) exists. Harder to deduplicate. O(n²).

Trap

Deduplication: skip i if nums[i] == nums[i-1]. Skip l if nums[l] == nums[l-1] after finding a triple.

126 Remove Duplicates from Sorted Array Easy

Given a sorted array, remove duplicates in-place and return the new length.

Signals

Two Pointers

Slow pointer writes unique values. Fast pointer scans. When different, advance slow and copy. O(n).

Pointer Walk

Same idea with a write index. Every unique element gets written to the next position.

Trap

Don't shift the entire array when removing — just overwrite with the slow pointer.

Related Micro Drills

8 quick recall drills to reinforce this topic.

123 Container with most water Pointer Manipulation 15s

Greedy two-pointer moves the shorter wall inward. O(n) time, O(1) space.

l, r = 0, len(h)-1
mx = 0
while l < r:
    mx = max(mx, min(h[l], h[r]) * (r - l))
    if h[l] < h[r]: l += 1
    else: r -= 1
88 Container with most water setup Pointer Manipulation 10s

Greedy narrowing of left/right pointers maximizes area. Proof relies on the shorter-wall argument.

l, r = 0, len(h) - 1
best = 0
while l < r:
    area = min(h[l], h[r]) * (r - l)
    best = max(best, area)
    if h[l] < h[r]: l += 1
    else: r -= 1
124 Trapping rain water Pointer Manipulation 15s

Two-pointer with running left/right max computes trapped water in O(n).

l, r = 0, len(h)-1
lm = rm = res = 0
while l < r:
    if h[l] < h[r]:
        lm = max(lm, h[l])
        res += lm - h[l]
        l += 1
    else:
        rm = max(rm, h[r])
        res += rm - h[r]
        r -= 1
84 Converging two pointers Pointer Manipulation 10s

Converging pointers shrink the search space from both ends. Core of two-sum-sorted and palindrome.

l, r = 0, len(a) - 1
while l < r:
    # process a[l], a[r]
    l += 1; r -= 1
125 3Sum skip duplicates Pointer Manipulation 15s

Sort + fix first + two-pointer on remainder. Skip duplicates at all three levels.

a.sort()
for i in range(len(a)-2):
    if i and a[i] == a[i-1]: continue
    l, r = i+1, len(a)-1
    while l < r:
        s = a[i] + a[l] + a[r]
        if s < 0: l += 1
        elif s > 0: r -= 1
        else:
            res.append([a[i], a[l], a[r]])
            l += 1
            while l < r and a[l] == a[l-1]: l += 1
87 Move zeros to end Pointer Manipulation 10s

Move-zeroes is remove-element followed by a fill. Classic two-pointer warm-up.

w = 0
for r in range(len(a)):
    if a[r] != 0:
        a[w] = a[r]
        w += 1
while w < len(a):
    a[w] = 0
    w += 1
126 Remove duplicates in-place Pointer Manipulation 10s

Write-pointer compaction for sorted arrays. Returns new length.

w = 1
for i in range(1, len(a)):
    if a[i] != a[i-1]:
        a[w] = a[i]
        w += 1
return w
8 Remove duplicates (sorted array) Pointer Manipulation 10s

Two-pointer write keeps unique elements in-place with O(1) space. Core array cleanup technique.

# Two-pointer write — keep unique in-place
w = 1
for r in range(1, len(a)):
    if a[r] != a[r - 1]:
        a[w] = a[r]
        w += 1
# a[:w] has unique elements