Currently available · INDIA
All writing
AlgorithmsJune 26, 2025·8 min read·0

Two Pointers — Complete Guide with Interactive Walkthroughs

Master the two pointers technique for arrays and strings — opposite ends, merge walk, palindrome checks, two sum on sorted input, and subsequence matching. Step-through animations included.

Two Pointers — Complete Guide with Interactive Walkthroughs

Two pointers is one of the most common patterns in coding interviews. You keep two integer indices moving along an array or string — usually called left/right or i/j — and make O(1) work per step. Done right, the whole algorithm stays linear.

This guide follows the same format as a structured algorithms course: we introduce the idea, then walk through classic examples with interactive animations. The walkthrough problems are for learning — you don't need to solve them cold on first read.

---

What Is Two Pointers?

Two pointers means using two index variables that move along an iterable (array or string). They never jump randomly — each step advances at least one pointer in a controlled way, which is why runtime stays O(n) (or O(n + m) for two inputs).

There are two main families:

PatternSetupWhen to use
Opposite endsleft = 0, right = n − 1, move inwardPalindrome, two sum on sorted array, container with most water
Parallel scani and j on one or two arrays, move forwardMerge sorted arrays, is subsequence, intersection of sorted lists

---

Pattern 1 — Opposite Ends (Move Inward)

Start one pointer at the first index and one at the last. Loop until they meet:

javascript
function fn(arr):
    left = 0
    right = arr.length - 1

    while left < right:
        # problem-specific logic
        # then: left++, or right--, or both

Why it's O(n): the pointers start n apart and move closer every iteration — at most O(n) loop iterations. If inner work is O(1), total time is O(n) and space is O(1).

---

Example 1 — Valid Palindrome

A string is a palindrome if it reads the same forward and backward ("racecar", "abcdcba").

After reversing, the first character must equal the last, the second must equal the second-last, and so on. Two pointers check pairs from both ends:

Valid Palindrome

Check if "racecar" reads the same forward and backward

Input

r
L
0
a
1
c
2
e
3
c
4
a
5
r
R
6
Step 1/4

Compare s[0] vs s[6]

'r' === 'r' ✓ — move inward

function isPalindrome(s) {
  let left = 0;
  let right = s.length - 1;

  while (left < right) {
    if (s[left] !== s[right]) return false;
    left++;
    right--;
  }

  return true;
}

isPalindrome("racecar"); // true
isPalindrome("abcde");   // false

Complexity: O(n) time, O(1) space — only two integers no matter how long the string.

---

Example 2 — Two Sum on a Sorted Array

Given a sorted array of unique integers and a target, return whether any pair sums to target. (LeetCode [167. Two Sum II](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/).)

Brute force checks every pair: O(n²). Because the array is sorted, opposite-end pointers do better:

  • If nums[left] + nums[right] > target → sum too big → right--
  • If sum < target → sum too small → left++
  • If sum === target → found

Two Sum II (Sorted Input)

nums = [1,2,4,6,8,9,14,15], target = 13

Input

1
L
0
2
1
4
2
6
3
8
4
9
5
14
6
15
R
7
Step 1/5target = 13

Sum = 1 + 15 = 16

16 > 13 → decrease sum, move right--

Why moving right is safe when sum is too large: nums[left] is the smallest value we can pair with nums[right]. If even that sum exceeds target, no smaller partner for nums[right] exists — so right can never be part of a solution. Symmetric logic applies when the sum is too small.

function twoSumSorted(nums, target) {
  let left = 0;
  let right = nums.length - 1;

  while (left < right) {
    const sum = nums[left] + nums[right];
    if (sum === target) return true;
    if (sum > target) right--;
    else left++;
  }

  return false;
}

twoSumSorted([1, 2, 4, 6, 8, 9, 14, 15], 13); // true (4 + 9)

Complexity: O(n) time, O(1) space.

---

Pattern 2 — Parallel Scan (Two Inputs)

When you have two sorted arrays (or a pattern + text), start i and j at index 0 and advance one or both:

javascript
function fn(arr1, arr2):
    i = j = 0
    while i < arr1.length AND j < arr2.length:
        # compare arr1[i] vs arr2[j]
        # advance i, j, or both

    # exhaust leftovers if needed

At most n + m pointer moves → O(n + m) when inner work is O(1).

---

Example 3 — Merge Two Sorted Arrays

Combine two sorted arrays into one sorted array.

Naive approach: concatenate + sort → O((n+m) log(n+m)). Two pointers merge in O(n + m):

Merge Two Sorted Arrays

Build [1,2,3,4,5,6] from [1,3,5] and [2,4,6]

arr1

1
i
0
3
1
5
2

arr2

2
j
0
4
1
6
2
Step 1/6

Compare 1 vs 2

Take 1 → ans = [1]

function mergeSorted(arr1, arr2) {
  const ans = [];
  let i = 0;
  let j = 0;

  while (i < arr1.length && j < arr2.length) {
    if (arr1[i] <= arr2[j]) ans.push(arr1[i++]);
    else ans.push(arr2[j++]);
  }

  while (i < arr1.length) ans.push(arr1[i++]);
  while (j < arr2.length) ans.push(arr2[j++]);

  return ans;
}

mergeSorted([1, 3, 5], [2, 4, 6]); // [1,2,3,4,5,6]

---

Example 4 — Is Subsequence

Given strings s and t, return whether s is a subsequence of t (LeetCode [392](https://leetcode.com/problems/is-subsequence/)).

"ace" is a subsequence of "abcde" — letters appear in order with gaps allowed. "aec" is not (wrong order).

Walk t with j. When s[i] === t[j], you "found" the next character of si++. Always j++. Success when i === s.length.

Is Subsequence

Is s = "ace" a subsequence of t = "abcde"?

s

a
i
0
c
1
e
2

t

a
j
0
b
1
c
2
d
3
e
4
Step 1/6

s[0] == t[0] ('a')

Match → i++

function isSubsequence(s, t) {
  let i = 0;
  let j = 0;

  while (i < s.length && j < t.length) {
    if (s[i] === t[j]) i++;
    j++;
  }

  return i === s.length;
}

isSubsequence("ace", "abcde"); // true
isSubsequence("aec", "abcde"); // false

Complexity: O(|s| + |t|) time, O(1) space.

---

Two Pointers vs Sliding Window

Both use two indices, but the intent differs:

Two pointersSliding window
GoalCompare or merge from two positionsMaintain a contiguous subarray/substring
Pointer movementOften opposite ends or two separate arraysUsually both move forward (L, R)
Classic problemsPalindrome, two sum II, merge, subsequenceMax sum size k, longest unique substring

Many problems blur the line — that's fine. The label matters less than recognizing linear movement with O(1) per step.

---

When the Template Shifts

The patterns above are guidelines, not laws:

  • Sometimes both pointers start at index 0 and move forward on the same array (e.g. remove duplicates in-place).
  • Sometimes you need three pointers (3Sum extends two sum with a fixed outer index).
  • Sorted input is often required for the opposite-ends trick; unsorted two sum needs a hash map instead.

Stay flexible — if the iterable structure suggests pairing ends or scanning two sequences in parallel, try two pointers first.

---

Complexity Cheat Sheet

ProblemTimeSpace
Valid palindromeO(n)O(1)
Two sum II (sorted)O(n)O(1)
Merge two sorted arraysO(n + m)O(1)*
Is subsequenceO(n + m)O(1)

* Output array not counted toward extra space.

---

Practice Problems (Easiest → Hardest)

---

Quick Reference — Pattern Picker

Pattern Picker

Click a node to trace the path — cards below update with your selection

pair from endstwo sequencesequal pairssorted + targetmax area/volumeboth sortedpattern in textTwo Pointers ProblemOpposite endsParallel scanCompare pairsSorted two sumMaximize metricMerge sortedSubsequence match

---

Key Takeaways

  1. Two indices, linear moves. Each pointer advances in a controlled way — that's the O(n) guarantee.
  2. Opposite ends when comparing pairs from both sides or the input is sorted.
  3. Parallel scan when merging or matching across two sequences.
  4. Decide which pointer moves before you code — write the rule in plain English first.
  5. Sorted input unlocks opposite ends for two sum; unsorted two sum needs a hash map.
  6. 3Sum = sort + outer index + two pointers — a natural extension of pattern 1.
  7. Step through the animations. If you can narrate each pointer move, you can implement it.

If this helped, pair it with the [Sliding Window Technique](/blog/sliding-window-technique) guide for contiguous subarray problems.

/ Stay in the loop

Get the next one in
your inbox.

New essays and field notes sent only when there's something worth sending. No tracking, no spam, easy unsubscribe.

Join · No spam · One-click unsubscribe