Introduction to Two Pointers Pattern

Two Pointers Pattern

The Two Pointers pattern is a powerful technique used to solve problems that involve finding relationships between elements in an array, string, or linked list.
Instead of using nested loops (O(n²) time), we use two pointer variables to iterate more efficiently, often achieving O(N) complexity.

When to Use

Main Variants

We’ll go through 6 core types of two pointer patterns, each with:

1. Opposite Direction Pointers (Start–End)

When to Use

How It Works

Example

Problem: Given a sorted array, find a pair that adds to a target sum.
Pseudocode:

def two_sum_sorted(arr, target):
    left, right = 0, len(arr) - 1
    while left < right:
        total = arr[left] + arr[right]
        if total == target:
            return [left, right]
        elif total < target:
            left += 1
        else:
            right -= 1
    return []

Complexity: O(N) time, O(1) space
LeetCode Examples:

2. Same Direction Pointers (Sliding Window)

When to Use

How It Works

Example

Problem: Longest substring without repeating characters.

def length_of_longest_substring(s):
    left = 0
    seen = {}
    max_len = 0

    for right in range(len(s)):
        if s[right] in seen and seen[s[right]] >= left:
            left = seen[s[right]] + 1
        seen[s[right]] = right
        max_len = max(max_len, right - left + 1)
    return max_len

Complexity: O(N) time, O(K) space (K = alphabet size)
LeetCode Examples:

3. Fast & Slow Pointers (Tortoise–Hare)

A two pointer algorithm where pointers move at different speeds. By moving at different speeds (say, in a cyclic LinkedList), the algorithm proves that the two pointers are bound to meet. The fast pointer should catch the slow pointer once both the pointers are in a cyclic loop. Commonly, the “fast” pointer moves two steps at a time, while the “slow” pointer moves one step at a time.

Example Scenario

Imagine two friends walking around a circular track:

When to Use

Key Scenarios Where This Pattern Shines

  1. Linked List Cycle Detection: Whenever a problem states, “Given a linked list, determine if it has a cycle,” you should think about the Fast & Slow Pointers pattern.
  2. Middle of a Linked List: If you need to find the middle node of a list in one pass, the Fast & Slow Pointers approach is perfect.
  3. Length of a Cycle: After detecting a cycle, you can continue with the fast pointer to measure the cycle length.
  4. Reorder or Split Linked List: When partitioning or rearranging a linked list, finding the middle node is often crucial.

How It Works

Example

Problem: Detect cycle in a linked list.

def has_cycle(head):
    slow, fast = head, head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow == fast:
            return True
    return False

Complexity: O(N) time, O(1) space
LeetCode Examples:

4. Partitioning Pointers

When to Use

How It Works

Example

Problem: Move zeroes to end.

def move_zeroes(nums):
    insert_pos = 0
    for num in nums:
        if num != 0:
            nums[insert_pos] = num
            insert_pos += 1
    for i in range(insert_pos, len(nums)):
        nums[i] = 0

LeetCode Examples:

5. K-Distance Apart

When to Use

How It Works

Example

Problem: Remove N-th node from end of list.

def remove_nth_from_end(head, n):
    dummy = ListNode(0, head)
    first = second = dummy
    for _ in range(n+1):
        first = first.next
    while first:
        first = first.next
        second = second.next
    second.next = second.next.next
    return dummy.next

LeetCode Examples:

6. Merge-Style Pointers

When to Use

How It Works

Example

Problem: Merge two sorted lists.

def merge_two_lists(l1, l2):
    dummy = ListNode()
    tail = dummy
    while l1 and l2:
        if l1.val < l2.val:
            tail.next, l1 = l1, l1.next
        else:
            tail.next, l2 = l2, l2.next
        tail = tail.next
    tail.next = l1 or l2
    return dummy.next

LeetCode Examples:

Quick Decision Table

Problem Type Recommended Pattern
Sorted + sum/diff check Opposite Direction
Sorted + merging/intersection Merge-Style
Subarray/substring constraint Sliding Window
Linked list cycle/middle Fast & Slow
N-th from end K-Distance Apart
In-place reorder Partitioning