The pattern behind half of all easy/medium array problems is the read/write two-pointer: one pointer reads through the array, another marks where the next “kept” element should be written. Move Zeroes is the canonical drill.
Problem. Given an array, move all
0s to the end while keeping the relative order of the non-zero elements. Do it in place — no copy.
See it run
Step through it. The slow pointer holds the next slot for a non-zero value; the fast pointer scans ahead. Every time fast lands on a non-zero, we put it at slow and advance slow.
The mental model
Think of two jobs happening at once:
- fast does the reading — it visits every element exactly once.
- slow does the writing — it only moves forward when it has placed a value.
Everything to the left of slow is already the compacted, zero-free prefix. When fast finishes, positions [slow, end) are guaranteed to be the leftover zeros, so you’re done — no second pass needed.
The code
function moveZeroes(nums) {
let slow = 0;
for (let fast = 0; fast < nums.length; fast++) {
if (nums[fast] !== 0) {
[nums[slow], nums[fast]] = [nums[fast], nums[slow]]; // swap
slow++;
}
}
return nums;
}
That’s the whole thing. The swap does double duty: it places the non-zero value at slow and carries whatever was at slow (a zero, once we’re past the prefix) out to fast.
Generalize the pattern
Once Move Zeroes clicks, these are the same problem with a different “keep” condition:
| Problem | ”Keep” condition |
|---|---|
| Remove Element | nums[fast] !== val |
| Remove Duplicates from Sorted Array | nums[fast] !== nums[slow - 1] |
| Partition array (Dutch flag) | three pointers: low / mid / high |
If you can write Move Zeroes from memory and explain the invariant (“everything left of slow is finalized”), you own this entire category.
Say it out loud (30s)
“I use two pointers — a write pointer
slowand a read pointerfast. I scan withfast; whenever I hit a non-zero I swap it toslowand bumpslow. The invariant is that everything left ofslowis the compacted non-zero prefix, so when the scan ends the zeros are already at the back. One pass, O(n) time, O(1) extra space — optimal.”