DSA Patterns: Two Pointers

LilSardarXLilSardarX
7 min read

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 = 13

    Step-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 0s, 1s, and 2s 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 go
  • right: where the next 2 should go
  • i: 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 with left, then move both left and i forward
  • If it's 1: just move i forward
  • If it's 2: swap with right, then move right back (but not i)

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 and right
  • 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.

11
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.