Algorithm

Move Zeroes

Two Pointer Pattern

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.

CONSTRAINTS
  • 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
EXAMPLE 1
Input: nums = [0,1,0,3,12]
Output: [1,3,12,0,0]
The non-zeros 1, 3, 12 stay in that order at the front; the two zeros end up at the back.
EXAMPLE 2
Input: nums = [0]
Output: [0]
A single zero has nothing to move past, so the array is unchanged.
EXAMPLE 3
Input: nums = [1,2,3]
Output: [1,2,3]
There are no zeros, so nothing moves and the order is preserved exactly.
Is it required to maintain the relative order of the non-zero elements?
Yes, if the non-zeroes were 1, 3, 12, they must remain 1, 3, 12 at the front of the array.
Can I just copy the non-zeroes to a new array and pad with zeros?
No, the prompt strictly requires you to do this in-place without making a copy of the array.
Is there a scenario where the array contains no zeros at all?
Yes, that is possible. Your algorithm should ideally handle that with no unnecessary writes.
Does the relative order of the zeros matter at the end?
No, because all zeros are identical, their relative order is irrelevant.

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.

The easy way that breaks a rule

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.

python
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 allowed
One pointer reads, another marks where to write

This 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.

python
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 keep
Crucial Notew 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.
Worked Example:nums = [0, 1, 0, 3, 12]
- w = 0. r=0 reads 0 → skip.
- r=1 reads 1 → keep. Swap slots 0 and 1: [1, 0, 0, 3, 12]. w = 1.
- r=2 reads 0 → skip.
- r=3 reads 3 → keep. Swap slots 1 and 3: [1, 3, 0, 0, 12]. w = 2.
- r=4 reads 12 → keep. Swap slots 2 and 4: [1, 3, 12, 0, 0]. w = 3.
- One pass done. Non-zeros [1, 3, 12] are packed in front, zeros trail behind.
The reusable skeleton — and when to reach for it

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 elements

Only 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.

What it costs

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.

Interactive Strategy Visualization

Two-Pointer Swap

In-Place Array Partitioning

ANCHOR
SCAN
0
0
1
1
0
2
3
3
12
4
PROCESSED IDX
0
Cur(0) is 0. Ignore. Placeholder waits.

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.

O(N) Time · O(N) Space Copy
O(N) Time · O(1) Space Write Pointer