DEV Community

Jayaprasanna Roddam
Jayaprasanna Roddam

Posted on

Two Pointers (Same Direction)

Overview

The Two Pointers (Same Direction) pattern uses two indices moving forward together through a data structure (usually an array or string).

Unlike opposite-direction pointers, both pointers only move left → right, but at different speeds or based on conditions.

This pattern is extremely common in linear-time optimizations.


When to Use Two Pointers (Same Direction)

Use this pattern when:

  • You process elements in order
  • You want to compare or relate two positions
  • You need to skip, filter, or track a range
  • You want to avoid nested loops (O(N²))

Typical Problem Statements

  • Remove duplicates from sorted array
  • Check if array is a subsequence of another
  • Longest substring without repeating characters
  • Merge two sorted arrays
  • Partition array based on condition
  • Fast & slow pointer problems (variant)

Core Intuition

Think of the pointers as:

  • Reader & Writer
  • Slow & Fast
  • Anchor & Explorer

One pointer represents a reference position, the other scans ahead.


General Algorithm Template

initialize slow = 0

for fast in range(0, n):
if condition satisfied:
perform operation using slow & fast
slow += 1

Both pointers move forward, but:

  • fast moves every iteration
  • slow moves only when needed

Key Properties

  • Time Complexity: O(N)
  • Space Complexity: O(1)
  • Works best on sorted arrays or strings
  • Avoids unnecessary comparisons

Example 1: Remove Duplicates from Sorted Array

Problem

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

Idea

  • fast scans elements
  • slow marks the position to write unique values

Code

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
Enter fullscreen mode Exit fullscreen mode

Example 2: Check Subsequence
Problem
Check if string s is a subsequence of string t.

Idea
slow tracks position in s

fast scans t

Code

def is_subsequence(s, t):
    slow = 0

    for fast in range(len(t)):
        if slow < len(s) and s[slow] == t[fast]:
            slow += 1

    return slow == len(s)
Enter fullscreen mode Exit fullscreen mode

Example 3: Longest Substring Without Repeating Characters
Problem
Find the length of the longest substring without repeating characters.

Idea
left and right both move forward

Use a set to maintain uniqueness

Code

def longest_unique_substring(s):
    seen = set()
    left = 0
    max_len = 0

    for right in range(len(s)):
        while s[right] in seen:
            seen.remove(s[left])
            left += 1

        seen.add(s[right])
        max_len = max(max_len, right - left + 1)

    return max_len
Enter fullscreen mode Exit fullscreen mode

Example 4: Merge Two Sorted Arrays
Problem
Merge two sorted arrays into one sorted array.

Idea
Pointer i for first array

Pointer j for second array

Both move forward

def merge_sorted(a, b):
    i = j = 0
    result = []

    while i < len(a) and j < len(b):
        if a[i] <= b[j]:
            result.append(a[i])
            i += 1
        else:
            result.append(b[j])
            j += 1

    result.extend(a[i:])
    result.extend(b[j:])
    return result
Enter fullscreen mode Exit fullscreen mode

Difference from Sliding Window
Feature Two Pointers (Same Direction) Sliding Window
Window Concept ❌ Optional ✅ Required
Condition Simple comparison Constraint-based
Use Case Filtering / matching Optimization
Example Remove duplicates Longest subarray

Common Mistakes
Moving both pointers blindly

Forgetting pointer bounds

Using it for unsorted data when order matters

Confusing it with opposite-direction pointers

How to Identify This Pattern
Ask yourself:

  • Do I need to scan forward once?
  • Can one pointer track progress?
  • Is order important?
  • Can I replace a nested loop? If yes → Two Pointers (Same Direction)

Mental Model
Imagine:

O

  • ne pointer walks
  • Another pointer records
  • Both move forward, never backward

Summary

  • Aspect Two Pointers (Same Direction)
  • Pointers Both move left → right
  • Speed Different
  • Time O(N)
  • Space O(1)
  • Best For Filtering, matching, merging

Final Thought
This pattern is the bridge between brute force and optimal solutions.
Once mastered, you’ll instinctively replace nested loops with clean linear scans.

Top comments (0)