Skip to content
Back to skills

Two Pointers

ASecurity

Two-pointer scans — opposite ends, same direction, fast/slow — for sorted arrays, partitioning, cycle detection.

  • 3 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added August 31, 2026
ai-agentsjavascriptpythongojava

Security analysis

A100/100

Scanned August 31, 2026

npx -y skills add 0bserver07/chimera --skill two-pointers --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Two Pointers?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Two Pointers
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/0bserver07-two-pointers/badge)](https://www.skillsdirectory.com/skills/0bserver07-two-pointers)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: algo-two-pointers
description: Two-pointer scans — opposite ends, same direction, fast/slow — for sorted arrays, partitioning, cycle detection.
when-to-use: "Sorted-array pair sums, palindromes, removing duplicates in place, linked-list cycle detection, partitioning."
---

## Two pointers — converging or chasing

Two-pointer scans turn many `O(n^2)` loops into `O(n)`. There are
three common shapes:

1. **Opposite ends** — `lo` at the start, `hi` at the end, walk
   inward (sorted-array pair sum, palindrome).
2. **Same direction** — both start at 0; `j` advances every step,
   `i` advances conditionally (in-place dedup, partition).
3. **Fast/slow** — `slow` advances by 1 step, `fast` by 2 (linked-
   list cycle detection, middle of list).

### Invariants

* The pointer pair encloses the *unprocessed* region of the input.
* Each pointer moves monotonically; no backtracking. That is what
  buys the `O(n)` amortisation.
* For opposite-ends on a sorted array: if `arr[lo] + arr[hi]` is
  too small, only `lo++` can help (any pair with the current `lo`
  paired against a smaller `hi` is even smaller).

### Complexity

* Time: `O(n)` for a single linear scan; `O(n^2)` for k-sum
  variants where one pointer is fixed and a two-pointer scan is
  done inside.
* Space: `O(1)` extra.

### Template — Python (opposite ends, sorted pair sum)

```python
def pair_sum_sorted(arr: list[int], target: int) -> tuple[int, int] | None:
    """Indices of two values in sorted ``arr`` summing to target."""
    lo, hi = 0, len(arr) - 1
    while lo < hi:
        s = arr[lo] + arr[hi]
        if s == target: return (lo, hi)
        if s < target: lo += 1
        else: hi -= 1
    return None
```

Note: requires sorted input. If the input is unsorted, hashing is
typically simpler; only use two-pointers when sorting is already
done or stable indices don't matter.

### Template — Python (same direction, in-place dedup)

```python
def dedup_sorted(arr: list[int]) -> int:
    """Compact sorted ``arr`` so the first ``k`` entries are unique. Returns k."""
    if not arr: return 0
    i = 0  # write index
    for j in range(1, len(arr)):
        if arr[j] != arr[i]:
            i += 1
            arr[i] = arr[j]
    return i + 1
```

The "write pointer + read pointer" form is the basis for all
in-place partitioning algorithms (Lomuto, Hoare).

### Template — JavaScript (Floyd's cycle detection)

```javascript
function hasCycle(head) {
  let slow = head, fast = head;
  while (fast && fast.next) {
    slow = slow.next;
    fast = fast.next.next;
    if (slow === fast) return true;
  }
  return false;
}
```

If the lists meet, there is a cycle. The mathematical proof: in a
cycle of length L, `fast` gains 1 step on `slow` per iteration, so
they meet within L iterations of `slow` entering the cycle.

### Common pitfalls

* **Forgetting the input must be sorted.** Two-pointers on
  unsorted input gives wrong answers — sort first, or use hashing.
* **Off-by-one on `hi = len(arr)` vs `hi = len(arr) - 1`.** Choose
  one convention; the templates above use inclusive `hi`. Mixing
  conventions causes index-out-of-range or missed pairs.
* **Mutating while iterating.** In dedup-style code, advance write
  pointer *before* writing, otherwise the first element gets
  overwritten.
* **Floyd's cycle detection with `head == null`.** Guard the loop
  with `fast && fast.next`.
* **Sliding-window mistaken for two-pointers.** Sliding window is
  same-direction with an explicit "shrink" step; two-pointers may
  not need shrinking. Pick the simpler one.

### Test corner cases

* Empty array.
* Single-element array (no pair possible / nothing to dedup).
* All-equal array (dedup compacts to length 1).
* Pair at the boundary (`lo == 0`, `hi == n - 1` matches).
* No matching pair.
* Linked list of length 1 with a self-loop (`head.next = head`).
* Linked list of length 2 with no cycle.

The dedup template generalises to any "compact while preserving
order under predicate P" — change the comparator and you get
remove-zeroes, remove-duplicates-keep-at-most-twice, etc.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.