Back to Python Examples

Move Zeros

A two-pointer pattern that modifies the array in-place with minimal writes.

Contents

  1. The Problem
  2. Solution 1 — Filter and Rebuild
  3. Solution 2 — Two Pointers In-Place
  4. Step-by-Step Trace
  5. Solution 3 — Minimal Writes

1. The Problem

Given a list, move all zeros to the end while maintaining the relative order of non-zero elements. Do it in-place without making a copy of the list.

# [0,1,0,3,12]  →  [1,3,12,0,0]
# [0,0,1]       →  [1,0,0]
# [0]           →  [0]

2. Solution 1 — Filter and Rebuild

def move_zeros_naive(nums):
    non_zeros = [x for x in nums if x != 0]
    zeros     = [0] * (len(nums) - len(non_zeros))
    nums[:]   = non_zeros + zeros  # Modify in-place via slice assignment

Clean and readable but creates extra lists — O(n) space. The in-place requirement means we should aim for O(1) extra space.

3. Solution 2 — Two Pointers In-Place

Keep a write pointer that tracks where the next non-zero should go. Walk a read pointer through the array; when it finds a non-zero, write it at write and advance both. Finally, fill the rest with zeros.

def move_zeros(nums):
    write = 0  # next position for a non-zero element

    # Pass 1: compact all non-zeros to the front
    for read in range(len(nums)):
        if nums[read] != 0:
            nums[write] = nums[read]
            write += 1

    # Pass 2: fill the tail with zeros
    for i in range(write, len(nums)):
        nums[i] = 0

a = [0, 1, 0, 3, 12]
move_zeros(a)
print(a)  # [1, 3, 12, 0, 0]
PropertyValue
TimeO(n)
SpaceO(1) — truly in-place

4. Step-by-Step Trace

Input: [0, 1, 0, 3, 12]

readnums[read]ActionwriteArray
00Skip (zero)0[0,1,0,3,12]
11Write 1 at [0], write++1[1,1,0,3,12]
20Skip (zero)1[1,1,0,3,12]
33Write 3 at [1], write++2[1,3,0,3,12]
412Write 12 at [2], write++3[1,3,12,3,12]

Fill from index 3 onward with zeros → [1, 3, 12, 0, 0]

5. Solution 3 — Minimal Writes (Swap)

Instead of writing and then zeroing, swap each non-zero with the element at write. Zeros are automatically pushed back without a second pass. Useful when writes are expensive.

def move_zeros_swap(nums):
    write = 0
    for read in range(len(nums)):
        if nums[read] != 0:
            nums[write], nums[read] = nums[read], nums[write]
            write += 1

b = [0, 1, 0, 3, 12]
move_zeros_swap(b)
print(b)  # [1, 3, 12, 0, 0]
The swap approach does more swaps than necessary (swapping zero with zero when write == read early on) but avoids the second zeroing pass. Both approaches are O(n) time, O(1) space.