Move Zeroes
Given an integer array nums, move all 0s to the end of it while maintaining the relative order of the non-zero elements. Note that you must do this in-place without making a copy of the array.
- 1 <= nums.length <= 10⁴
- -2³¹ <= nums[i] <= 2³¹ - 1
- Must be done in-place without making a copy of the array
- Minimize the total number of operations
nums = [0,1,0,3,12][1,3,12,0,0]nums = [0][0]nums = [1,2,3][1,2,3]We take an array and push every 0 to the back, while the non-zero numbers keep their original order — [0, 1, 0, 3, 12] becomes [1, 3, 12, 0, 0]. Two words in the statement do all the constraining: in place (no building a second array) and keep the order of the non-zeros.
Make a fresh array, copy the non-zeros into it in order, then pad the rest with zeros, and copy it back. Simple and correct — but it allocates a whole second array of size N, which is exactly the O(N) extra space the problem tells us to avoid.
non_zeros = [x for x in nums if x != 0]
result = non_zeros + [0] * (len(nums) - len(non_zeros))
nums[:] = result # a second array of size N — not allowedThis is the same-direction two-pointer family again — two pointers both walking forward through the array — but in a new shape, and it's the shape you'll reuse most often for arrays. (You first met the family in Middle of the Linked List, where the two pointers moved at different speeds. Here the twist is what makes the slower pointer move.)
Keep a write position — call it w — that marks where the next number we want to keep should go. Then run a second pointer across every slot in the array. That second pointer is the reader: it looks at each value in turn. When it finds a non-zero (a value worth keeping), we put that value at position w and bump w forward by one. When it finds a zero, we do nothing and just keep reading. The reader always advances; w advances only when we actually keep something. That gap between the two — reader ahead, w lagging by however many zeros we've skipped — is the whole trick. By the time the reader finishes, every kept number has been packed to the front in order, and w marks where the zeros begin.
The cleanest version does it with a swap: when the reader finds a non-zero, swap it into the w slot. Swapping (instead of overwriting) automatically carries the zero that was sitting at w out to the reader's old spot, so the zeros drift to the back for free and nothing is lost.
w = 0 # next slot to fill with a kept value
for r in range(len(nums)): # r reads every slot
if nums[r] != 0: # a value worth keeping?
nums[w], nums[r] = nums[r], nums[w] # place it at w, push the zero out
w += 1 # advance w only on a keepw moves only when we keep a value, never on a zero — that is what lets it fall behind the reader by exactly the count of zeros seen so far, which is precisely where the next kept value belongs. The non-zeros keep their relative order because the reader visits them left to right and we place them at w in that same sequence — we never reorder among them.Strip this problem away and a template remains that solves a whole run of "clean up an array in one pass, in place" tasks:
w = 0
for r in range(len(nums)):
if <keep nums[r]?>:
nums[w] = nums[r] # (or swap)
w += 1
# first w slots are the kept elementsOnly two things change from problem to problem: what counts as a keeper (here: "not zero"), and what to record and return (here: nothing to return; sometimes it's w, the count of kept items). Fill those two slots and you've solved the next one. In Remove Duplicates from Sorted Array the keeper rule becomes "different from the last value I kept," and w is the answer. The signals that should make this pattern fire in an unseen problem: you're asked to remove / move / dedupe elements of an array, in place, with O(1) extra space, and keeping the surviving elements' order. That exact combination almost always means a read pointer sweeping while a write pointer trails.
One sweep, no second array: O(N) time, O(1) space. The naive copy was also O(N) time but paid O(N) space — the write pointer buys that back. Train the reflex: whenever you want to filter or compact an array without allocating a new one, don't delete-and-shift (that's O(N) per removal); let a write pointer trail a read pointer and rebuild the array in place as you go.
Two-Pointer Swap
In-Place Array Partitioning
In-Place Efficiency
We use two pointers to partition the array. The anchor pointer tracks the boundary where the next non-zero element should be placed, while the scan pointer explores new values.
Optimal Operation Count
By swapping non-zeroes with zeroes, we eliminate the need for a second pass to fill remaining slots. This achieves O(N) time with only O(1) auxiliary space.