Two-Pointer Template
The two-pointer technique is the linear-time answer to many sorted-array problems: pair sums, palindrome checks, in-place reversal, partition. This snippet covers the opposite-ends sweep for sorted-pair targets, the reverse-in-place pattern, and the slow-fast pointer used for in-place mutation. Each fits in 10 lines and runs in O(n) time, O(1) extra space.
418 views
3
function twoSumSorted(arr, target) {
let lo = 0;
let hi = arr.length - 1;
while (lo < hi) {
const sum = arr[lo] + arr[hi];
if (sum === target) return [lo, hi];
if (sum < target) lo++;
else hi--;
}
return null;
}
console.log(twoSumSorted([1, 3, 4, 6, 9], 10)); // [0, 4] (1 + 9 = 10)
console.log(twoSumSorted([2, 4, 7, 11], 13)); // [0, 3] (2 + 11 = 13)
console.log(twoSumSorted([1, 2, 3], 99)); // nullWhen the array is sorted, the two-pointer sweep replaces the O(n^2) double loop with a single O(n) pass. The invariant is: every pair containing arr[lo] with an index < hi has already been considered, so if the current sum is too small, no smaller hi can help and we must move lo up. Symmetric reasoning lets us shrink hi when the sum is too large. Use this for twoSum on sorted input, finding pair-with-given-difference, or any problem that monotonically narrows on a sorted range.
function reverse(arr) {
let lo = 0;
let hi = arr.length - 1;
while (lo < hi) {
[arr[lo], arr[hi]] = [arr[hi], arr[lo]];
lo++;
hi--;
}
return arr;
}
console.log(reverse([1, 2, 3, 4, 5])); // [5, 4, 3, 2, 1]
console.log(reverse(['a', 'b', 'c'])); // ['c', 'b', 'a']
console.log(reverse([])); // []Reversing in place uses the same two-pointer skeleton but swaps instead of moving conditionally. The loop runs n / 2 iterations, each doing one swap, so total work is O(n) with O(1) extra space. The empty-array case is handled by the loop condition without any special case. Anywhere you would reach for array.slice().reverse() you can use this version to save the allocation, which matters when the array is huge or you want to mutate in place anyway.
function removeZeros(arr) {
let write = 0;
for (let read = 0; read < arr.length; read++) {
if (arr[read] !== 0) {
arr[write] = arr[read];
write++;
}
}
arr.length = write;
return arr;
}
console.log(removeZeros([1, 0, 2, 0, 3, 0, 4])); // [1, 2, 3, 4]
console.log(removeZeros([0, 0, 0])); // []
console.log(removeZeros([5, 6, 7])); // [5, 6, 7]A slow-fast (or 'two pointers, same direction') sweep is the right tool for in-place compaction: filtering, deduplication of sorted arrays, and the classic 'move zeros to the end' problem. The read pointer scans every element, while write only advances when something is kept. Setting arr.length = write truncates the tail in O(1), avoiding a fresh allocation. This pattern also forms the inner loop of partition routines used by quicksort and Dutch-flag-style problems.
