Move All Zeroes to End is a classic two-pointer warm-up. It teaches a move you will reuse constantly: a slow write-pointer that compacts the values you want to keep, while a fast read-pointer scans everything — all in place, no extra array.
Problem. Given an array of integers nums, move all the 0s to the end while keeping the
relative order of the non-zero elements. Do it in place, without making a copy of the array.
Example: nums = [5, 0, 9, 0, 0, 3] → [5, 9, 3, 0, 0, 0]. The non-zeros 5, 9, 3 keep their order;
the three zeros are pushed to the back.
The slow way first
The obvious idea: build a brand-new array, copy every non-zero into it, then pad the rest with zeros. That works and it is O(n) time — but it uses O(n) extra space, and the problem asks us to do it in place.
The question to ask: can I keep one pointer aimed at the next spot a non-zero should land, and just scan the rest with another? That is exactly the two-pointer compaction trick — and it needs no second array.
The idea: a write slot that chases the non-zeros
Keep two indices. slow is the next slot that should hold a non-zero. fast walks across every element. When fast finds a non-zero, swap it into the slow slot and advance slow. When fast finds a zero, do nothing and let fast move on — the zero gets left behind and ends up at the back automatically.
The key insight: because slow only advances when we place a non-zero, everything before slow is the non-zeros in their original order, and everything from slow onward becomes the zeros — for free.
Walk through it
Step through the animation. fast (below) scans left to right; slow (above) marks the write slot. When fast hits 5 at index 0, slow and fast agree, so it stays. The zero at index 1 is skipped. When fast reaches 9, we swap it back into slow = 1, sliding the zero rightward. The same happens for 3. After one pass the array reads [5, 9, 3, 0, 0, 0].
Pseudocode
slow = 0 # next slot for a non-zero
for fast from 0 to len(nums) - 1:
if nums[fast] is not zero:
swap nums[slow] and nums[fast] # place the non-zero, push a zero right
slow = slow + 1 # the slot is filled; move it forward
return numsThe Python solution
def move_zeroes(nums):
slow = 0
for fast in range(len(nums)):
if nums[fast] != 0:
nums[slow], nums[fast] = nums[fast], nums[slow]
slow += 1
return numsslowstarts at 0 — the first slot that should receive a non-zero value.fastruns over every index with theforloop; it never skips ahead.- Line 4 is the decision: only a non-zero triggers a placement.
- Line 5 is the swap — the non-zero at
fastlands in theslowslot, and whatever was atslow(a zero, once we are past the front) slides out tofast. - We bump
slowonly after a placement, so it always points at the next empty slot. Zeros never advance it, which is why they pile up at the end.
Complexity
| Case | Time | Notes |
|---|---|---|
| Copy into a new array | O(n) (moderate) | simple, but uses O(n) extra space |
| Two-pointer swap (this solution) | O(n) (moderate) | one pass, in place |
O(1) (fast)We make a single pass and never allocate a second array, so the work is O(n) time and O(1) extra space. That trade — a write-pointer compacting the keepers while a read-pointer scans — is one of the most reused array patterns there is.
When this pattern shows up
Any time a problem says "move / remove / keep elements that pass a test, in place, preserving order,"
reach for the slow write-pointer pattern. Remove Element, Remove Duplicates from a Sorted Array, and
Move Zeroes are all the same move: fast reads, and slow only advances when you decide to keep a value.
Advance slow only when you place a value, never on a zero. If you bump slow every iteration it
just tracks fast, the swap becomes a no-op, and nothing moves. The zeros end up at the back precisely
because slow lags behind fast by the count of zeros seen so far.
Practice
For nums = [5, 0, 9, 0, 0, 3], when fast reaches the 9 at index 2, where does slow point and what happens?
1. What does the slow pointer represent?
2. When does slow advance?
3. Why is the relative order of the non-zeros preserved?
4. What is the extra space used by the two-pointer solution?