# How to Apply the Two Pointers Technique for Array Problems: 3 Patterns Explained

> Master the two pointers technique for array problems. Learn 3 essential patterns to solve common challenges with O(N) time and O(1) space complexity, optimizing your code.

- Repository: [lucifer/leetcode](https://github.com/azl397985856/leetcode)
- Tags: tutorial
- Published: 2026-03-06

---

**The two pointers technique uses two indices to traverse an array in a single pass, achieving O(N) time complexity and O(1) space complexity by inspecting each element at most once.**

This algorithmic framework is extensively documented in the `azl397985856/leetcode` repository, where it serves as the foundation for solving hundreds of array and string problems. Whether you are searching for pairs, detecting cycles, or optimizing subarray calculations, understanding how to apply the two pointers technique for array problems will significantly reduce your solution's memory footprint and improve runtime performance.

## What Is the Two Pointers Technique?

The two pointers technique is a strategy that maintains two references (indices) into a data structure simultaneously. Unlike brute-force approaches that use nested loops creating O(N²) complexity, this method moves one or both pointers monotonically toward a solution.

Because arrays offer O(1) random access, the technique is particularly effective for array problems. The pointers can jump to any position instantly, and by moving them in a coordinated fashion—either toward each other, at different speeds, or maintaining a fixed distance—you can encode problem-specific invariants directly into the traversal logic.

## Three Canonical Patterns for Array Problems

According to the source documentation in [`91/two-pointers.md`](https://github.com/azl397985856/leetcode/blob/main/91/two-pointers.md), nearly every array problem solvable with two pointers falls into one of three distinct patterns.

### Fast and Slow Pointers (Tortoise and Hare)

In this pattern, one pointer advances one step per iteration while the other advances two steps (or a larger fixed step). This creates a relative speed differential that reveals structural properties of the data.

**Typical use cases:**
- Detecting cycles in linked lists or arrays
- Finding the middle element of a sequence
- Locating the k-th element from the end

**Implementation example:** See [`problems/141.Linked-List-Cycle.md`](https://github.com/azl397985856/leetcode/blob/main/problems/141.Linked-List-Cycle.md) for the classic cycle detection algorithm.

### Left and Right Pointers (Two Ends)

Here, pointers initialize at opposite boundaries (`left = 0`, `right = n - 1`) and move toward each other based on comparison logic. This pattern exploits sorted order or symmetric properties.

**Typical use cases:**
- Binary search on sorted arrays
- Two-sum problems on sorted data
- Reversing arrays or checking palindromes

**Implementation example:** The [`problems/977.Squares-of-a-Sorted-Array.md`](https://github.com/azl397985856/leetcode/blob/main/problems/977.Squares-of-a-Sorted-Array.md) solution demonstrates this pattern effectively.

### Fixed-Distance Sliding Window

This pattern maintains pointers with a constant offset or expands/contracts a window to satisfy constraints. Both pointers typically move in the same direction (left to right), with the right pointer expanding the window and the left pointer contracting it when constraints are violated.

**Typical use cases:**
- Finding longest/shortest subarrays with specific sums
- String problems involving character constraints (e.g., longest substring with k distinct characters)
- Removing duplicates from sorted arrays

**Implementation example:** See [`problems/80.remove-duplicates-from-sorted-array-ii.md`](https://github.com/azl397985856/leetcode/blob/main/problems/80.remove-duplicates-from-sorted-array-ii.md) for a sliding window approach to in-place array modification.

## Practical Implementation Examples

The following code examples from the repository demonstrate each pattern in production-ready implementations.

### Two Sum on a Sorted Array (Left-Right Pattern)

This Python implementation from the repository's two-pointer documentation solves the classic two-sum problem on sorted data using the left-right pattern:

```python
def two_sum_sorted(nums, target):
    """Return indices (0-based) of two numbers that add up to target.
    Precondition: `nums` is sorted."""
    l, r = 0, len(nums) - 1
    while l < r:
        s = nums[l] + nums[r]
        if s == target:
            return l, r
        elif s < target:
            l += 1          # need a larger sum → move left pointer right

        else:
            r -= 1          # need a smaller sum → move right pointer left

    return None           # no solution

```

The algorithm initializes pointers at both ends and moves them inward based on the sum comparison, guaranteeing O(N) time with O(1) auxiliary space.

### Longest Substring with K Distinct Characters (Sliding Window)

This JavaScript implementation demonstrates the fixed-distance (expanding/contracting) sliding window pattern for string problems:

```javascript
function longestSubstrKDistinct(s, k) {
    const map = new Map();         // char → count in current window
    let l = 0, maxLen = 0;

    for (let r = 0; r < s.length; r++) {
        map.set(s[r], (map.get(s[r]) ?? 0) + 1);
        // shrink window until we have at most k distinct chars
        while (map.size > k) {
            const cnt = map.get(s[l]) - 1;
            if (cnt === 0) map.delete(s[l]);
            else map.set(s[l], cnt);
            l++;
        }
        maxLen = Math.max(maxLen, r - l + 1);
    }
    return maxLen;
}

```

The right pointer (`r`) expands the window to include new characters, while the left pointer (`l`) contracts it when the constraint (at most `k` distinct characters) is violated.

### Detecting a Cycle in a Linked List (Fast/Slow Pattern)

This Java implementation from [`problems/141.Linked-List-Cycle.md`](https://github.com/azl397985856/leetcode/blob/main/problems/141.Linked-List-Cycle.md) uses the tortoise-and-hare algorithm to detect cycles with O(1) space:

```java
public boolean hasCycle(ListNode head) {
    ListNode slow = head, fast = head;
    while (fast != null && fast.next != null) {
        slow = slow.next;          // move 1 step
        fast = fast.next.next;     // move 2 steps
        if (slow == fast) return true; // meeting point → cycle
    }
    return false;
}

```

If a cycle exists, the fast pointer (moving two steps) will eventually meet the slow pointer (moving one step) inside the cycle. If the fast pointer reaches the end, no cycle exists.

## Key Repository Files and Resources

The `azl397985856/leetcode` repository contains extensive documentation and implementations of these patterns:

- **[`91/two-pointers.md`](https://github.com/azl397985856/leetcode/blob/main/91/two-pointers.md)** – Core documentation covering all three pointer patterns with templates and complexity analysis.
- **[`problems/80.remove-duplicates-from-sorted-array-ii.md`](https://github.com/azl397985856/leetcode/blob/main/problems/80.remove-duplicates-from-sorted-array-ii.md)** – Sliding window implementation for in-place array manipulation.
- **[`problems/977.Squares-of-a-Sorted-Array.md`](https://github.com/azl397985856/leetcode/blob/main/problems/977.Squares-of-a-Sorted-Array.md)** – Left-right pointer technique applied to sorted array transformation.
- **[`problems/141.Linked-List-Cycle.md`](https://github.com/azl397985856/leetcode/blob/main/problems/141.Linked-List-Cycle.md)** – Fast/slow pointer pattern for cycle detection.
- **[`problems/16.3Sum-Closest.md`](https://github.com/azl397985856/leetcode/blob/main/problems/16.3Sum-Closest.md)** – Extension of the two-ends pattern to three-pointer problems.

## Summary

- The **two pointers technique** reduces array problems from O(N²) brute-force to O(N) time while maintaining O(1) space complexity.
- **Three canonical patterns** cover most scenarios: Fast/Slow for cycles and middle-finding, Left-Right for sorted arrays and two-sum problems, and Fixed-Distance Sliding Window for substring/subarray constraints.
- **Implementation requires** identifying the invariant (what condition must hold), initializing pointers according to the chosen pattern, and moving them monotonically toward the solution.
- The `azl397985856/leetcode` repository provides production-ready templates in [`91/two-pointers.md`](https://github.com/azl397985856/leetcode/blob/main/91/two-pointers.md) and worked examples across multiple problem files.

## Frequently Asked Questions

### What is the time and space complexity of the two pointers technique?

The two pointers technique typically achieves **O(N) time complexity** because each pointer traverses the array at most once, inspecting each element a constant number of times. The **space complexity is O(1)** because only two index variables (and possibly a few scalar variables) are stored regardless of input size, making it an in-place algorithm.

### When should I use the fast and slow pointer pattern versus the left and right pattern?

Use the **fast and slow pattern** when you need to detect cycles, find the middle of a sequence, or identify the k-th element from the end without knowing the total length beforehand. Use the **left and right pattern** when the array is sorted or when you need to find pairs that satisfy a specific condition (like two-sum), as starting at both ends allows you to eliminate one element from consideration with each comparison.

### Can the two pointers technique work on unsorted arrays?

Yes, the two pointers technique works on unsorted arrays, but the **left-right pattern specifically requires sorted data** to guarantee correctness when moving pointers inward. For unsorted arrays, you typically use the **sliding window** (fixed-distance) pattern or the **fast/slow** pattern, neither of which depends on sorted order. If you need the left-right behavior on unsorted data, you must sort the array first (O(N log N)), then apply the technique.

### How do I choose between a fixed-size window and a variable-size sliding window?

Choose a **fixed-size window** when the problem explicitly requires subarrays or substrings of a specific length *k*, allowing both pointers to advance at the same rate. Choose a **variable-size (dynamic) sliding window** when you need to find the longest or shortest valid subarray that satisfies a constraint (like "at most k distinct characters"), requiring the left pointer to move conditionally while the right pointer expands the window.