Master Coding Interviews : Part 2 ( Two Pointers Pattern )

Published: (March 30, 2026 at 12:32 PM EDT)
7 min read
Source: Dev.to

Source: Dev.to

If you read Part 1 of this series, you already know how a single pattern can transform a slow, brute‑force solution into an elegant, optimized one. Today we’ll do the same thing — but with a different tool in our belt: the Two Pointers pattern.

This pattern shows up everywhere in coding interviews. Once you recognize it, you’ll start seeing it in problems you might have struggled with before.


What is the Two Pointers Pattern?

The idea is simple: instead of using one index to iterate through your data structure, you use two. These pointers move through the array (or string) according to specific conditions, and together they help you find the answer without needing nested loops.

There are two main variants you’ll encounter:

VariantDescription
Opposite‑direction pointersOne pointer starts at the beginning, the other at the end. They move toward each other until they meet.
Same‑direction pointers (fast & slow)Both pointers start at the same end, but move at different speeds or under different conditions.

Let’s jump straight into a problem to see the first variant in action.


Problem 1 — Two Sum II: Input Array Is Sorted

Two Sum II: Input Array Is Sorted

Given a 1‑indexed array of integers numbers that is already sorted in non‑decreasing order, find two numbers such that they add up to a specific target number. Return the indices of the two numbers as an integer array [index1, index2] of length 2.

Brute‑Force Solution

class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        for i in range(len(numbers)):
            for j in range(i + 1, len(numbers)):
                if numbers[i] + numbers[j] == target:
                    return [i + 1, j + 1]   # 1‑indexed result
        return []

Time complexity: O(n²) – for an array of 30 000 elements this is potentially 900 million comparisons.
Result: Time Limit Exceeded on large inputs (LeetCode). In production, a quadratic loop over large datasets is a performance bottleneck.

Optimization through the Two Pointers Pattern

The key insight we’re missing in the brute‑force approach is that the array is already sorted. That hint lets us use the Two Pointers pattern:

  1. Place one pointer at the beginning (left) and one at the end (right).
  2. Compute their sum.
    • If the sum equals the target → return the indices.
    • If the sum is too large → move right leftward to get a smaller value.
    • If the sum is too small → move left rightward to get a larger value.

Each iteration eliminates a candidate, and because the pointers converge, we find the answer (or confirm none exists) in a single pass.

class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        left = 0                     # start of the array
        right = len(numbers) - 1     # end of the array

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

            if current_sum == target:
                # Return 1‑indexed result
                return [left + 1, right + 1]

            if current_sum > target:
                # sum too large → move right pointer left
                right -= 1
            else:
                # sum too small → move left pointer right
                left += 1

        return []   # problem guarantees a solution, but good practice

Time complexity: O(n)
Space complexity: O(1)

A dramatic improvement over the brute‑force approach, and it passes LeetCode with no issues.


Variant 2 — Same‑Direction Pointers

The second variant doesn’t converge from opposite ends. Instead, both pointers start from the same side and move in the same direction, but at different speeds or under different conditions. This is perfect for problems where you need to restructure an array in‑place or detect a specific pattern while scanning forward.

Let’s look at a classic example.


Problem 2 — Remove Duplicates from Sorted Array

Remove Duplicates from Sorted Array

Given an integer array nums sorted in non‑decreasing order, remove the duplicates in‑place such that each unique element appears only once. Return the number of unique elements k.

The naïve instinct might be to use a set to collect unique values and rebuild the array — but the problem explicitly requires an in‑place solution with O(1) extra space. The same‑direction two‑pointers technique (often called slow and fast pointers) solves this efficiently.

(The rest of the solution continues in the next article.)

In‑Place Solution Using O(1) Extra Memory

A set would cost us O(n) space, which defeats the purpose.

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        seen = set()
        result = []

        for num in nums:
            if num not in seen:
                seen.add(num)
                result.append(num)

        # This doesn't satisfy the in‑place constraint
        for i in range(len(result)):
            nums[i] = result[i]

        return len(result)

Beyond the space‑constraint violation, this approach creates auxiliary data structures we don’t need.
The Two Pointers pattern gives us a cleaner — and compliant — solution.

Optimization: Same‑Direction Two Pointers

Idea: Use a slow pointer to track the position where the next unique element should be written, and a fast pointer to scan ahead through the array looking for new unique values. Whenever fast finds a value different from what slow is pointing at, we advance slow and write that new value there.

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        # 'slow' marks the boundary of our unique‑elements section
        slow = 0

        # 'fast' scans ahead to discover new unique elements
        for fast in range(1, len(nums)):
            # When fast finds a value different from slow's position,
            # a new unique element has been discovered
            if nums[fast] != nums[slow]:
                slow += 1                  # Expand the unique section
                nums[slow] = nums[fast]    # Write the new unique value in place

        # slow is now the index of the last unique element
        # so the count of unique elements is slow + 1
        return slow + 1
  • Time complexity: O(n)fast makes a single pass through the array.
  • Space complexity: O(1) — everything is done in‑place, no extra data structures.

The elegance here is that the two pointers work as a team: fast does the exploration, slow does the writing. They never interfere with each other because slow can only be at or behind fast.


When to Use This Pattern

  • When dealing with sorted arrays or strings (a big hint for opposite‑direction pointers).
  • When the problem asks you to find a pair (or triplet) of elements that satisfy a condition.
  • When you need to restructure or deduplicate an array in‑place with O(1) space.
  • When the brute‑force solution involves nested loops over the same linear data structure — that’s almost always a signal that two pointers can collapse it to O(n).

What’s Next? Practice

Head over to LeetCode and try the Two Pointers problem list — there are over 200 problems tagged with this pattern. Start with the easy ones to build your instinct, then move into medium difficulty.

A Few Good Ones to Try Next

  • Container With Most Water (Medium) — opposite‑direction pointers.
  • 3Sum (Medium) — two pointers inside an outer loop.
  • Linked List Cycle (Easy) — fast & slow pointers on a linked list.

Note: As with the Sliding Window pattern, Two Pointers isn’t always the only valid approach. It’s a powerful tool when certain conditions are met — sorted data, in‑place constraints, pair/subset problems — but always think about whether the pattern genuinely fits before reaching for it.

See you in the next article for another coding pattern!

0 views
Back to Blog

Related posts

Read more »