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:
-
fastmoves every iteration -
slowmoves 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
-
fastscans elements -
slowmarks 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
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)
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
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
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)