Two pointers is the first real “algorithmic pattern”

Learn the two pointers pattern with intuition, patterns, and code so you can crush array/string questions.

Kodetra TechnologiesKodetra Technologies
5 min read
Jan 8, 2026
0 views
Two pointers is the first real “algorithmic pattern”

Two pointers is one of those patterns that quietly shows up in half of the “medium” LeetCode problems and a surprising number of real systems tasks involving streaming, parsing, or array-like data structures. It’s simple to learn, powerful in practice, and often the difference between O(n2)O(n2) brute force and O(n)O(n) production-grade code.

Below is a developer-focused introduction: when to use two pointers, core strategies, and concrete examples in JavaScript and Python you can adapt immediately.


What Is the Two Pointers Technique?

At its core, the two pointers technique means maintaining two indices (or iterators) over a sequence (array, string, linked list) and moving them in a coordinated way to satisfy some condition.

Typical characteristics:

  • Time complexity often drops from O(n2)O(n2) (nested loops) to O(n)O(n).
  • Space complexity is usually O(1)O(1) extra, aside from outputs.
  • Works best on sequential structures and often shines when the data is sorted or can be treated as a stream.

You’ll most commonly see it in:

  • Pair problems: “find two numbers that…”
  • Interval/window problems: “longest/shortest subarray/substring that…”
  • Linked list problems: “find middle node”, “detect cycle”, “reorder list”.

Core Strategies for Two Pointers

Most two-pointer solutions fall into a few patterns that are worth memorizing.

  • Opposite ends (inward traversal):Pointers start at the beginning and end of a sequence and move toward each other.Great for pair sums in sorted arrays, palindrome checks, and “squeeze” problems.
    • Pointers start at the beginning and end of a sequence and move toward each other.
    • Great for pair sums in sorted arrays, palindrome checks, and “squeeze” problems.
  • Same direction (sliding / shrinking):Both pointers move left→right, maintaining a window or scanning two sequences.​Used for subarray/substring constraints, merging sorted arrays, or matching sequences.
    • Both pointers move left→right, maintaining a window or scanning two sequences.
    • Used for subarray/substring constraints, merging sorted arrays, or matching sequences.
  • Fast–slow (different speeds):One pointer moves faster (2x) than the other.Used for cycle detection and finding the middle of a linked list.
    • One pointer moves faster (2x) than the other.
    • Used for cycle detection and finding the middle of a linked list.

Instead of memorizing problems, think of these as templates: “Do I need a pair, a window, or structure-aware traversal?”.


Strategy 1: Opposite Ends on Sorted Arrays

Use case

You have a sorted array and need to find a pair (or reason about pairs) that satisfies a condition, most frequently a target sum.

Example problem

Given a sorted array nums and an integer target, return true if there exist two numbers that add up to target, otherwise false.

Intuition

  • Put left at the start, right at the end.
  • Compute sum = nums[left] + nums[right].
  • If sum is too small, move left right to increase it; if too large, move right left to decrease it.

This leverages the sorted order to prune large parts of the search space.

JavaScript implementation

js// Two-sum in a sorted array using opposite-end pointers
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;         // Found a pair
    } else if (sum < target) {
      left++;              // Need a bigger sum
    } else {
      right--;             // Need a smaller sum
    }
  }

  return false;            // No pair found
}

// Example
console.log(twoSumSorted([-3, -1, 0, 1, 2], -2)); // true

This runs in O(n)O(n) time and O(1)O(1) space, versus O(n2)O(n2) if you tried all pairs with nested loops.

Variation: Trapping or squeezing

The same “squeeze from both ends” pattern is used for problems like:

  • Checking if a string is a palindrome (compare s[left] and s[right], then move inward).
  • Squaring and sorting an already sorted array of positives and negatives.

Strategy 2: Sliding Window with Two Pointers

Use case

You need a contiguous subarray/substring that satisfies a condition:

  • Longest substring without repeating characters.
  • Smallest subarray with sum ≥ target.
  • Max number of consecutive 1s, etc.

Here, both pointers move in the same direction, defining a window [left, right).

Example problem

Given an array of positive integers nums and an integer target, find the minimal length of a contiguous subarray of which the sum is ≥ target. If there is no such subarray, return 0.

Intuition

  • Expand right to grow the window until the sum is ≥ target.
  • Once it’s valid, shrink from the left by moving left to try to minimize the length.
  • Repeat until right reaches the end.

Python implementation

python# Minimum size subarray sum using sliding window
def min_subarray_len(target, nums):
    left = 0
    window_sum = 0
    min_len = float("inf")

    for right in range(len(nums)):
        window_sum += nums[right]

        # Shrink from the left while window is "good enough"
        while window_sum >= target:
            min_len = min(min_len, right - left + 1)
            window_sum -= nums[left]
            left += 1

    return 0 if min_len == float("inf") else min_len

# Example
print(min_subarray_len(7, [2,3,1,2,4,3]))  # 2 (subarray [4,3])

This is a classic sliding-window two-pointer pattern with O(n)O(n) time and O(1)O(1) extra space.


Strategy 3: Fast–Slow Pointers on Linked Lists

Use case

You’re working with linked lists and need to reason about relative positions without extra memory:

  • Detect a cycle (Floyd’s tortoise and hare).
  • Find the middle node.
  • Split or reorder lists.

Example problem

Given the head of a singly linked list, determine if it has a cycle.

Intuition

  • Maintain two pointers:slow moves one step at a time.fast moves two steps at a time.
    • slow moves one step at a time.
    • fast moves two steps at a time.
  • If there's a cycle, they will eventually meet inside it.
  • If fast reaches null, there is no cycle.

Python implementation

pythonclass ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def has_cycle(head):
    slow = fast = head

    while fast and fast.next:
        slow = slow.next          # 1 step
        fast = fast.next.next     # 2 steps

        if slow == fast:
            return True           # Met inside a cycle

    return False                  # Reached end, no cycle

This technique runs in linear time and constant space, avoiding extra structures like hash sets.


Mini Case Study: Refactoring Brute Force to Two Pointers

Imagine a coding challenge:

“Given a sorted array of integers and a target, return all unique pairs (a, b) such that a + b = target.”

A naive approach would:

  • Loop i from 0..n-1.
  • For each i, loop j from i+1..n-1.
  • Collect pairs where nums[i] + nums[j] == target.That’s O(n2)O(n2) and redundant, especially on large n.

Refactor with two pointers:

js// Return all unique pairs that sum to target in a sorted array
function twoSumPairs(nums, target) {
  const result = [];
  let left = 0;
  let right = nums.length - 1;

  while (left < right) {
    const sum = nums[left] + nums[right];

    if (sum === target) {
      result.push([nums[left], nums[right]]);

      // Skip duplicates to keep pairs unique
      const leftVal = nums[left];
      const rightVal = nums[right];
      while (left < right && nums[left] === leftVal) left++;
      while (left < right && nums[right] === rightVal) right--;
    } else if (sum < target) {
      left++;
    } else {
      right--;
    }
  }

  return result;
}

// Example
console.log(twoSumPairs([1,1,2,2,3,4,4,5], 6));
// [ [2,4], [1,5] ] depending on ordering

This version is linear in the size of the array and handles uniqueness by skipping contiguous duplicates—a common pattern in two-pointer solutions on sorted inputs.


Practical Tips and Gotchas

A few patterns and pitfalls that come up repeatedly in interviews and production code.

  • Initialize correctly:Opposite ends: left = 0, right = n - 1.Sliding window: left = 0, grow right in a loop.Fast–slow: both at head.
    • Opposite ends: left = 0, right = n - 1.
    • Sliding window: left = 0, grow right in a loop.
    • Fast–slow: both at head.
  • Define invariants clearly:For sliding windows, track what your window means (sum ≥ target, all unique chars, etc.).For opposite ends, know which direction increases or decreases your metric.
    • For sliding windows, track what your window means (sum ≥ target, all unique chars, etc.).
    • For opposite ends, know which direction increases or decreases your metric.
  • Handle termination conditions:Opposite ends usually stop at left >= right.Sliding window iterates right once and moves left only forward.Fast–slow stops when pointers meet or fast reaches the end.
    • Opposite ends usually stop at left >= right.
    • Sliding window iterates right once and moves left only forward.
    • Fast–slow stops when pointers meet or fast reaches the end.
  • Watch for off-by-one and empty inputs:Always consider [], [1], and “no solution” cases.Decide whether you return indices, values, or a boolean.
    • Always consider [], [1], and “no solution” cases.
    • Decide whether you return indices, values, or a boolean.
  • Prefer two pointers over hash maps when:Input is sorted or cheaply sortable.You want predictable memory usage and cache-friendly, linear scans.
    • Input is sorted or cheaply sortable.
    • You want predictable memory usage and cache-friendly, linear scans.

Conclusion

Two pointers is one of the highest-leverage patterns you can master for algorithmic interviews and real-world data-processing tasks. It turns many “obvious but slow” nested loops into clean, linear-time solutions by coordinating two indices instead of brute-forcing all pairs.

If you’re practicing, focus on these steps:

  • Spot sorted or sequential data and pair/window constraints.
  • Decide whether you’re using opposite ends, sliding window, or fast–slow.
  • Write down your invariants and termination conditions, then implement carefully.

From there, problems like two-sum variants, palindromes, substring windows, and linked-list cycles all start to look pleasantly similar—which is exactly what you want when the clock is ticking in an interview or performance-critical code review.

Kodetra Technologies

Kodetra Technologies

Kodetra Technologies is a software development company that specializes in creating custom software solutions, mobile apps, and websites that help businesses achieve their goals.

0 followers

Comments

Sign in to join the discussion

No comments yet. Be the first to comment!