2026-09-17 — Move Zeroes
Difficulty: 🟢 Easy Pattern: Two pointers (read/write) Time-box: 20 minutes
Problem
Given an integer array, move all 0s to the end in place, keeping the
relative order of the non-zero elements.
Examples
[0, 1, 0, 3, 12] -> [1, 3, 12, 0, 0]
[0] -> [0]
[1, 2, 3] -> [1, 2, 3] # nothing to move
[0, 0, 1] -> [1, 0, 0]
Constraints
- Do not allocate a second array — O(1) extra space.
- The order of the non-zero elements must be preserved.
- Minimise the number of writes if you can.
Hint (open only if stuck after 10 minutes)
Building a new list of non-zeroes and padding with zeroes is easy but uses O(n) extra space.
Instead, use two indices moving in the same direction. Let read scan every
element, and let write mark where the next non-zero belongs. write only
advances when you actually place something.
After read finishes, what does everything from write onwards have to be?
Solution
def move_zeroes(nums: list[int]) -> None:
"""Move all zeroes to the end, in place, preserving order."""
write = 0
# Pass 1: compact every non-zero toward the front.
for read in range(len(nums)):
if nums[read] != 0:
nums[write] = nums[read]
write += 1
# Pass 2: everything from `write` on must be zero.
for i in range(write, len(nums)):
nums[i] = 0
How it works. write lags behind read by exactly the number of zeroes
seen so far. The first pass copies the non-zeroes forward into their final
positions; the second fills the tail. Because read only ever moves forward and
write never overtakes it, no unprocessed value is ever overwritten.
A one-pass variant using a swap:
def move_zeroes(nums: list[int]) -> None:
write = 0
for read in range(len(nums)):
if nums[read] != 0:
nums[write], nums[read] = nums[read], nums[write]
write += 1
This is one pass, but it performs a swap per non-zero element rather than a single write, so it is not automatically faster — for an array that is mostly zeroes it does fewer writes; for an array with few zeroes it does more.
Complexity. Time: O(n) — each element is examined once (twice in the two-pass version, still linear). Space: O(1) — two indices, no allocation.
Edge cases that matter.
- Empty array — both loops simply do not run.
- All zeroes — write stays 0, the second pass rewrites everything.
- No zeroes — every element is copied onto itself, which is harmless.
- A single element, zero or not.
Common wrong answer. Removing zeroes while iterating:
for i, n in enumerate(nums):
if n == 0:
nums.remove(n) # WRONG
nums.append(0)
Mutating a list while iterating it skips elements, so [0, 0, 1] comes out
wrong. It is also O(n²), because remove shifts the whole tail each time.
Found a mistake? Edit this page on GitHub.