DSA Patterns: Two Pointers

The first pattern, and probably the most fundamental one Iβve come across, is Two Pointers. It helps convert brute-force O(n^2) solutions into clean O(n) ones β especially when you're scanning for pairs, optimizing space, or processing input in-place.
Letβs break it down.
π When to Use Two Pointers
Use this when:
- The array is sorted
- You need to find a pair of elements that satisfy some condition
- You want to avoid nested loops
- You're scanning from both ends toward the middle
- You need to partition or rearrange elements in-place
π§° Two Pointers Templates
β Classic Opposite-Ends Pattern (e.g., Two Sum in Sorted Array)
Initialize l = 0
Initialize r = end of array
while l < r:
Compute sum or condition using array[l] and array[r]
if condition is satisfied:
Do something (e.g., return result)
else if value too small:
Move l forward
else:
Move r backward
β In-Place Partitioning Pattern (e.g., Dutch National Flag, Reverse String)
Initialize l = 0
Initialize r = end of array
while l < r:
Swap or process array[l] and array[r] as needed
Move l and/or r depending on the logic
- Use the first version when scanning from both ends for a pair.
- Use the second when modifying the array in-place using swaps (partitioning-style problems).
π§ Note: While most Two Pointers problems involve arrays or strings, the pattern also shows up in:
- Linked lists (cycle detection, merging)
- Multi-array traversal
- Tree traversal with parent-child alignment
- Simulated race conditions or relative motion
π Core Idea
Instead of checking every pair, we move two pointers from either end of the array. At each step, we:
- Look at the two elements
- Decide which pointer to move
Eliminate unnecessary pairs from the search
This works because the array is sorted, so moving a pointer means skipping many possibilities at once.
π§ͺ Sample Problem: Two Sum (Sorted Array)
Problem:
Given a sorted array and a target, return the pair of elements that add up to the target.Input:
nums = [1, 3, 4, 6, 8, 10, 13], target = 13Step-by-Step Walkthrough
Step 1: Start with two pointers
left right
β β
[1, 3, 4, 6, 8, 10, 13] β 1 + 13 = 14
Too big β move right backward
Step 2:
left right
β β
[1, 3, 4, 6, 8, 10, 13] β 1 + 10 = 11
Too small β move left forward
Step 3:
left right
β β
[1, 3, 4, 6, 8, 10, 13] β 3 + 10 = 13 β
Answer found!
β± Time & Space Complexity
- Time: O(n)
- Space: O(1)
π§ͺ Sample Problem 2: Container With Most Water (Medium)
You're given an array where each element represents the height of a vertical line on the x-axis. The task is to choose two lines that, together with the x-axis, form a container that holds the most water.
Brute force would mean checking every pair of lines and calculating the area β O(nΒ²).
But this is a perfect use case for Two Pointers.
Here's the key idea:
- Start with two pointers β one at the beginning (
left
) and one at the end (right
) of the array. - At every step, compute the area between the two lines they point to.
- To try and improve this area, move the pointer pointing to the shorter line, hoping to find a taller one.
Why move the shorter one? Because the water level is always limited by the shorter line, not the longer one. So moving the taller one is pointless β it canβt increase the result.
Letβs walk through a dry run to see how this works in action.
Input: [1, 8, 6, 2, 5, 4, 8, 3, 7]
Step 1: Start with both ends
left right
β β
[ 1, 8, 6, 2, 5, 4, 8, 3, 7 ] area = width * min(height) = 8 * min(1,7) = 8
β Store area = 8
Here's the key insight:
- The amount of water is limited by the shorter line.
- So there's no point keeping the shorter one. It can't help create a better area.
β
Action: Move the shorter pointer (left
) forward
Step 2: Move left to 8
left right
β β
[ 1, 8, 6, 2, 5, 4, 8, 3, 7 ] area = 7 * min(8,7) = 49
β New max area = 49
Now shorter height is 7 β move right
inward
Step 3: Move right to 3
left right
β β
[ 1, 8, 6, 2, 5, 4, 8, 3, 7 ] area = 6 * min(8,3) = 18
Max stays at 49
Again, 3
is shorter β move right
(Repeat until pointers meet)
Final max area = 49 (between heights 8 and 7 at indices 1 and 8)
π§ Why This Works
At each step, we eliminate one pointer's possibility by moving the shorter one. We do this only once per index β O(n) time.
β Brute force would check every pair β This method eliminates all pairs that canβt possibly beat the current area
π― Bonus Use Case: Partitioning Arrays
In some problems, instead of searching for a pair, you're asked to rearrange elements in-place. A classic example is the Dutch National Flag problem (also known as Sort Colors).
You're given an array of 0
s, 1
s, and 2
s in random order.
Your job is to sort them in one pass, without using any extra space.
This is where a variation of the two-pointer technique shines. In fact, here we use three pointers:
left
: where the next 0 should goright
: where the next 2 should goi
: current element we're evaluating
The idea is to walk through the array and:
- Push 0s to the front (
left
zone) - Push 2s to the end (
right
zone) - Leave 1s in the middle
At each step, we examine nums[i]
and perform one of the following:
- If it's
0
: swap withleft
, then move bothleft
andi
forward - If it's
1
: just movei
forward - If it's
2
: swap withright
, then moveright
back (but noti
)
Let's walk through the dry run.
Step-by-Step Walkthrough: Sort Colors (Dutch National Flag)
Step 0: Initial State
left right
β β
[ 2, 1, 2, 0, 1, 0 ]
i
nums[i] = 2 β swap with right
, move right
back
Do NOT move i
since we need to check the new value at i
Step 1: After swapping i and right
left right
β β
[ 0, 1, 2, 0, 1, 2 ]
i
nums[i] = 0 β swap with left
, move both left
and i
forward
Step 2:
left right
β β
[ 0, 1, 2, 0, 1, 2 ]
i
nums[i] = 1 β just move i
forward
Step 3:
left right
β β
[ 0, 1, 2, 0, 1, 2 ]
i
nums[i] = 2 β swap with right
, move right
back
Do NOT move i
yet β must check the new value again
Step 4: After swap
left right
β β
[ 0, 1, 1, 0, 2, 2 ]
i
nums[i] = 1 β move i
forward
Step 5:
left right
β β
[ 0, 1, 1, 0, 2, 2 ]
i
nums[i] = 0 β swap with left
, move both left
and i
forward
Final State:
left right
β β
[ 0, 0, 1, 1, 2, 2 ]
i
β Done β the array is sorted.
Time: O(n) Space: O(1)
π§ͺ More Classic Problems (Two Pointers Sketches)
βοΈ Valid Palindrome
- Check if a string is a palindrome by comparing chars at
left
andright
- Move inward as long as characters match
- Skip non-alphanumeric if needed
Example: "A man, a plan, a canal: Panama"
- After stripping and lowering: "amanaplanacanalpanama"
- Use Two Pointers to compare each side:
a == a
,m == m
, ...
Time: O(n), Space: O(1)
βοΈ Reverse String (In-Place)
- Swap characters from both ends until they meet
- No extra space required
Input: ['h', 'e', 'l', 'l', 'o']
left right
β β
['h', 'e', 'l', 'l', 'o']
After swap β ['o', 'e', 'l', 'l', 'h']
Time: O(n), Space: O(1)
β Final Summary: When to Use Two Pointers
Use this pattern when:
- You're looking for pairs or symmetric checks
- The input is sorted, or can be sorted
- You need an in-place transformation or scan
- You want to eliminate combinations without brute-force
β Core Problems to Master:
- Two Sum (Sorted)
- Container With Most Water
- 3Sum (Fix i, use two pointers)
- Valid Palindrome
- Reverse String
- Sort Colors (partitioning-style variant)
The key isn't just writing code with two variables β it's knowing what each pointer represents, and why you're moving one over the other.
Once you internalize that, Two Pointers becomes a go-to tool in your Leetcode toolkit.
Subscribe to my newsletter
Read articles from LilSardarX directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

LilSardarX
LilSardarX
Iβm figuring things out in tech β and blogging along the way. I write what I learn, so I can revise it later and maybe help you avoid a few rabbit holes.