Two-pointer scans — opposite ends, same direction, fast/slow — for sorted arrays, partitioning, cycle detection.
Scanned 8/31/2026
Install to Claude Code
npx -y skills add 0bserver07/chimera --skill two-pointers --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Two Pointers?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/0bserver07-two-pointers)More formats (shields.io, HTML) on the badges page.
---
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.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!