The Two Pointer technique is one of those rare algorithmic tools that feels like cheating once you truly understand it. Problems that look like they need nested loops — O(n²) brute force — suddenly collapse into a single O(n) pass. This article covers everything: the intuition, the patterns, step-by-step walkthroughs, and a map of when to actually use it.

What Is the Two Pointer Technique?

At its core, the two pointer approach uses two index variables (pointers) that traverse a data structure — usually an array or string — simultaneously. Instead of checking every pair of elements with two nested loops, you use the positions of these two pointers cleverly so that you only ever do one pass through the data.

The key insight is that in many problems, you can eliminate large chunks of the search space based on a comparison between what the pointers currently point at. This is why the approach is so powerful.

Think of two pointers like two fingers scanning a book — one from the left margin, one from the right. Depending on what each finger sees, you decide which one to move inward. You never need to compare every possible pair of pages.

The Three Main Flavors

Opposite Ends (Converging)

Left pointer starts at index 0, right pointer starts at the last index. They move toward each other. Classic for sorted-array pair problems.

Same Direction (Sliding)

Both pointers start at index 0 and move forward. The gap between them forms a window. Classic for subarray/substring problems.

Fast & Slow (Floyd's)

One pointer moves one step at a time; the other moves two. Classic for cycle detection in linked lists.

Multi-array / Two Sequences

One pointer per array. Used to merge or compare two sorted arrays simultaneously. Classic in merge sort and diff algorithms.

Example 1 — Two Sum in a Sorted Array

Problem: Given a sorted array and a target number, find two elements that add up to the target. Return their indices (1-indexed).

Array: [2, 7, 11, 15]  |  Target: 9

Step 1 — Initial Position
L
R
2
7
11
15
Left pointer
Right pointer
Match found
StepLRarr[L]arr[R]SumAction
10321517sum > 9 → move R left
20221113sum > 9 → move R left
301279✅ sum == target → return [1, 2]
Step 3 — Match Found
L
R
2
7
11
15
Pair found: 2 + 7 = 9

Python Code — Two Sum (Sorted)

python — Two Sum (Sorted)
def two_sum_sorted(nums, target):
    left = 0
    right = len(nums) - 1

    while left < right:
        current_sum = nums[left] + nums[right]

        if current_sum == target:
            return [left + 1, right + 1]
        elif current_sum < target:
            left += 1
        else:
            right -= 1

    return []

nums = [2, 7, 11, 15]
target = 9
print(two_sum_sorted(nums, target))  # Output: [1, 2]
Time Complexity
O(n)
Single pass through the array
Space Complexity
O(1)
Only two integer variables used

Example 2 — Valid Palindrome

Problem: Given a string, check whether it reads the same forwards and backwards (ignoring non-alphanumeric characters and case).

Input: "A man, a plan, a canal: Panama"Output: True

python — Valid Palindrome
def is_palindrome(s):
    left = 0
    right = len(s) - 1

    while left < right:
        while left < right and not s[left].isalnum():
            left += 1
        while left < right and not s[right].isalnum():
            right -= 1

        if s[left].lower() != s[right].lower():
            return False

        left += 1
        right -= 1

    return True

print(is_palindrome("A man, a plan, a canal: Panama"))  # True
print(is_palindrome("race a car"))                       # False

Example 3 — Remove Duplicates from Sorted Array

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

python — Remove Duplicates
def remove_duplicates(nums):
    if not nums:
        return 0

    slow = 0

    for fast in range(1, len(nums)):
        if nums[fast] != nums[slow]:
            slow += 1
            nums[slow] = nums[fast]

    return slow + 1

nums = [1, 1, 2, 3, 3, 3, 4, 5, 5]
k = remove_duplicates(nums)
print(k)         # 5
print(nums[:k])  # [1, 2, 3, 4, 5]
fastslownums[fast]nums[slow]Action
1011Same → skip
2021Different → slow=1, write 2
3132Different → slow=2, write 3
4233Same → skip
5233Same → skip
6243Different → slow=3, write 4
7354Different → slow=4, write 5
8455Same → skip

Example 4 — Container With Most Water

Problem: Given an array height, find two lines that form a container holding the most water.

Key insight: Area = min(height[L], height[R]) × (R - L). Move the shorter side inward.

python — Container With Most Water
def max_water(height):
    left = 0
    right = len(height) - 1
    max_area = 0

    while left < right:
        width = right - left
        current_height = min(height[left], height[right])
        area = width * current_height
        max_area = max(max_area, area)

        if height[left] < height[right]:
            left += 1
        else:
            right -= 1

    return max_area

height = [1, 8, 6, 2, 5, 4, 8, 3, 7]
print(max_water(height))  # Output: 49

Why move the shorter side? Because if you move the taller side, the width decreases AND the height stays limited by the same shorter side — area can only get worse. Moving the shorter side is the only chance to find a taller wall that could compensate for the reduced width.

When to Apply the Two Pointer Technique

Good Fit

Find if any pair sums to K in a sorted array, check palindrome, merge sorted arrays, detect linked list cycle, remove duplicates in-place.

Not a Fit

Problems needing random access or hash-based lookups, unsorted data where sorting changes the answer, tree/graph traversals.

Bonus — 3Sum (Extending Two Pointers)

python — 3Sum Problem
def three_sum(nums):
    nums.sort()
    result = []

    for i in range(len(nums) - 2):
        if i > 0 and nums[i] == nums[i - 1]:
            continue

        left = i + 1
        right = len(nums) - 1

        while left < right:
            total = nums[i] + nums[left] + nums[right]

            if total == 0:
                result.append([nums[i], nums[left], nums[right]])
                while left < right and nums[left] == nums[left + 1]:
                    left += 1
                while left < right and nums[right] == nums[right - 1]:
                    right -= 1
                left += 1
                right -= 1
            elif total < 0:
                left += 1
            else:
                right -= 1

    return result

nums = [-1, 0, 1, 2, -1, -4]
print(three_sum(nums))  # [[-1, -1, 2], [-1, 0, 1]]
Time Complexity
O(n²)
Outer loop × inner two-pointer pass
vs Brute Force
O(n³)
Three nested loops without two pointers

Common Mistakes to Avoid

Applying two pointers to an unsorted array — it won't work unless you sort first. The logic of "move left to increase, move right to decrease" only holds in sorted order.

Forgetting the loop termination condition left < right. If both pointers reach the same index, you shouldn't be comparing an element with itself.

Not skipping duplicate values in 3Sum / K-Sum problems — this will give you duplicate triplets in the result.

Confusing two pointers with sliding window — both use two indices, but sliding window expands/contracts based on a constraint, not a fixed comparison rule.

Quick Reference — Problems to Practice

ProblemPatternDifficulty
Two Sum II (sorted input)ConvergingEasy
Valid PalindromeConvergingEasy
Remove Duplicates from Sorted ArraySame DirectionEasy
Move ZerosSame DirectionEasy
Container With Most WaterConvergingMedium
3SumFix + ConvergingMedium
Trapping Rain WaterConvergingHard
Linked List Cycle DetectionFast & SlowEasy
Find Middle of Linked ListFast & SlowEasy
Minimum Window SubstringSliding WindowHard

Wrapping Up

The two pointer technique is not a magic trick — it's a deliberate reduction of the search space. Every time you move a pointer, you're making a guarantee: "I know for certain this region can't hold a better answer." That guarantee comes from the structure of the data and the property you're searching for.

Once you internalize that idea, you'll start recognizing two-pointer problems on sight. Start with the easy problems in the practice table, implement each one from scratch, and trace through your pointer movements manually on paper at least once. That tactile understanding is what makes the technique click.

Pick any two problems from the practice table above. Implement them from scratch without looking at any code. Trace through your pointer movements step by step on paper first. If you can do that for three problems in a row, two pointers is officially in your toolkit.

Next up: explore the Sliding Window technique — two pointers' close cousin for subarray and substring optimization problems.