Move Zeroes
Move all zeroes in an array to the end in place, keeping the relative order of the non-zero elements.
Open on LeetCode ↗Intuition
The common instinct is to swap on every non-zero element. That is correct, but it does needless writes — a swap is two stores where one would do, and when the array has no zeroes at all you end up swapping every element with itself. Worse is the variation people reach for when they are trying to be clever: swap the zero at the front with a non-zero found from the back. That version passes the 'zeroes at the end' half of the test and silently violates the other half, because it REORDERS the non-zero elements, which the problem explicitly forbids. The fix is to stop thinking in swaps. Keep a write pointer w and scan with k; whenever nums[k] is non-zero, overwrite nums[w] with it and advance w. Because you scan strictly left to right and only ever copy forward, the non-zeroes land in exactly the order they appeared — order is preserved by construction, not by care. The invariant is that nums[0:w] always holds every non-zero seen so far, in order; after the scan, fill from w to the end with zeroes.
A write pointer trailing a read pointer — the stable-partition idiom. Non-zero values are copied forward in order, then the tail is padded with zeros. The write index never overtakes the read index, so nothing is lost.
Approach
One write pointer, one scanner
w marks the next slot that should receive a non-zero value; k walks the whole array. Everything left of w is finished and correct, everything from w up to k is stale data that will be overwritten or zeroed later. Framing it this way removes the swap entirely, because you never need to preserve what is sitting at nums[w] — it has either already been copied forward or it is a zero you were going to discard anyway.
Copy non-zeroes forward
For each k, if nums[k] is non-zero, assign nums[w] = nums[k] and increment w. Zeroes are simply skipped, which leaves w behind and lets the next non-zero close the gap. Since w never exceeds k, the write can never clobber a value that has not been read yet — that is what makes the single-pass in-place version safe.
Pad the tail with zeroes
After the scan, w equals the count of non-zero elements, and everything from w to the end is leftover junk from the original array. Write zeroes over that range. This second pass is what makes the whole thing O(n) with two cheap sweeps instead of one clever but fragile one, and it keeps the total number of writes at most n.
Solution & live demo
Common pitfalls
Swapping instead of copying
nums[w], nums[k] = nums[k], nums[w]
nums[w] = nums[k] w += 1
Swapping happens to work here since the displaced value is always a zero, but it does twice the writes and obscures why the pattern is safe. The copy-then-pad form generalises to any stable partition.
Filling the tail from the wrong index
for i in range(len(nums) - count_zeros, len(nums)):
while w < len(nums):
nums[w] = 0
w += 1Tracking a separate zero count is one more thing to keep in sync. After the first pass w already points at the first unwritten slot, so it is exactly where the padding begins.
Deleting zeros in place
for v in nums:
if v == 0: nums.remove(v)if nums[k] != 0:
nums[w] = nums[k]Mutating a list while iterating it skips elements, and each remove is O(n) making the whole thing quadratic. The two-pointer pass is linear and touches each element once.
Edge cases
The first pass never writes and w stays 0, so the padding loop rewrites the whole array with zeroes — already correct, just redundant.
w tracks k exactly, so every write is nums[k] = nums[k], and the padding loop does not run. The array is unchanged.
Either it is non-zero and copied to slot 0, or it is a zero and the pad loop writes 0 back. Both are no-ops in effect.
The non-zeroes copy onto themselves and the tail is re-zeroed. Correct, and it costs nothing extra beyond the writes.