Advertisement
Home Data Structure Arrays Move All Zeros to the End of an Array

Move All Zeros to the End of an Array

1
Advertisement

Title: Move All Zeros to the End of an Array

Moving all the zeros in an array to the end, while keeping the other elements in their original order, is a classic warm-up problem for arrays and a staple of coding interviews. In this guide, you will learn a clean, in-place Java solution that runs in linear time and uses no extra memory, along with the thinking that goes into getting it right under interview pressure.

The real trick is to stop thinking about zeros as something to delete, and start thinking about non-zeros as something to collect. Once that flip happens, the whole algorithm becomes a single forward pass that writes keepers into their final positions.

Understanding the Problem

You are given an integer array. The task is to move every zero to the end of the array so that all non-zero elements appear first, in the same relative order they had in the input. The transformation must happen in place, meaning you may not allocate a second array as the final result.

Visually, if we start with the input below:

Java
int[] arr = {1, 0, 2, 0, 3, 4, 0, 5};
// After moveZeros(arr): {1, 2, 3, 4, 5, 0, 0, 0}

The expected final array is {1, 2, 3, 4, 5, 0, 0, 0}. The values 1, 2, 3, 4, 5 appear in exactly the same order they appeared in the input, and the three zeros are pushed to the back.

Approach to Move Zeros to the End

The cleanest approach uses a two-pointer technique. One pointer, writeIndex, marks the next slot that should hold a non-zero element. A second pointer, readIndex, scans the whole array once. We never think about zeros directly; we only decide whether to copy each element forward.

Here are the steps:

  1. Initialize writeIndex to 0. This is the next slot that will receive a non-zero element.
  2. Iterate readIndex from 0 to arr.length - 1.
  3. If arr[readIndex] is non-zero, copy it into arr[writeIndex] and increment writeIndex.
  4. If arr[readIndex] is zero, do nothing and let readIndex advance on its own.
  5. After the first pass, every non-zero has been packed into arr[0..writeIndex-1] in input order.
  6. Fill the remaining slots arr[writeIndex..length-1] with 0 to complete the result.

Java Program to Move Zeros to the End

Here is the full, compilable Java program that applies the two-pointer approach:

Java
import java.util.Arrays;

public class MoveZerosToEnd {

    public static void moveZeros(int[] arr) {
        int writeIndex = 0;

        for (int readIndex = 0; readIndex < arr.length; readIndex++) {
            if (arr[readIndex] != 0) {
                arr[writeIndex++] = arr[readIndex];
            }
        }

        while (writeIndex < arr.length) {
            arr[writeIndex++] = 0;
        }
    }

    public static void main(String[] args) {
        int[] arr = {1, 0, 2, 0, 3, 4, 0, 5};
        moveZeros(arr);

        System.out.println("Result: " + Arrays.toString(arr));
    }
}

Explanation of the Program

The method moveZeros takes the array by reference and mutates it in place, so the caller sees the result in the same array it passed in. We never allocate a new array, which is exactly the in-place requirement an interviewer is listening for.

The for loop is the compacting pass. On every non-zero element it writes to arr[writeIndex] and then increments writeIndex using the post-increment operator writeIndex++. Because readIndex is always at least as large as writeIndex, we never overwrite an element we have not yet read, so the write pointer trails the read pointer safely.

The while loop pads the remainder with zeros. After the first pass, writeIndex points to the first slot that still holds a stale value from the old positions, so we overwrite from there to the end with 0.

Step-by-Step Trace

Let us trace the algorithm on the input arr = {4, 0, 1, 0, 2}, which has non-zeros at indices 0, 2, 4 and zeros at indices 1, 3. The target output is {4, 1, 2, 0, 0}.

Iteration (readIndex)arr[readIndex]Non-zero?writeIndex (before)arr after this stepwriteIndex (after)
04yes0{4, 0, 1, 0, 2}1
10no1{4, 0, 1, 0, 2}1
21yes1{4, 1, 1, 0, 2}2
30no2{4, 1, 1, 0, 2}2
42yes2{4, 1, 2, 0, 2}3

After the for loop ends, the while pass overwrites arr[3] and arr[4] with 0, producing the final result {4, 1, 2, 0, 0}. At the end of each for-loop iteration, arr[0..writeIndex-1] holds every non-zero element seen so far, in the input order.

Examples

Let us run the method on three shapes of input that exercise different branches of the algorithm.

Example 1: Zeros Clustered at the Start

When the array starts with a run of zeros, every non-zero slides to the front and the trailing slots are filled with zeros.

Java
int[] arr = {0, 0, 0, 1, 2};
moveZeros(arr);
// Output: [1, 2, 0, 0, 0]

Example 2: No Zeros at All

If the array has no zeros, writeIndex ends equal to arr.length and the while loop never executes, so the array is unchanged.

Java
int[] arr = {1, 2, 3, 4};
moveZeros(arr);
// Output: [1, 2, 3, 4]

Example 3: Zeros Scattered Through the Array

With zeros interleaved among non-zero values, the two-pointer compaction cleanly collects every non-zero in its original order and pushes the zeros to the tail.

Java
int[] arr = {0, 5, 0, 0, 7, 0, 8};
moveZeros(arr);
// Output: [5, 7, 8, 0, 0, 0, 0]

Common Mistakes to Avoid

These are the mistakes learners most often make on this problem. Each one is subtle enough that the code compiles and runs, but produces the wrong result for at least one shape of input.

  • Only swapping the first zero with the first non-zero – works on {0, 1} but corrupts order on {0, 2, 1} because a single swap breaks the relative order of the non-zeros. Use a running writeIndex instead so every non-zero moves forward in input order.
  • Skipping the trailing zero fill – after the compacting pass the tail still holds stale copies of the shifted non-zeros, so {1, 0, 2} becomes {1, 2, 2} rather than {1, 2, 0}. Follow the for loop with a while loop that overwrites arr[writeIndex..length-1] with 0.
  • Decrementing readIndex after a shift – once readIndex revisits a slot it just wrote to, the loop never terminates on inputs with trailing zeros. Advance readIndex unconditionally in the for header and let writeIndex move independently.
  • Initializing writeIndex to 1 – shifts the entire packed region right by one, so the earliest non-zero never reaches index 0. Start writeIndex at 0 so the first non-zero lands at arr[0].

Edge Cases to Test

Walk through this checklist whenever you implement or review the solution.

  • Empty array
  • Single-element array holding zero
  • Single-element array holding a non-zero
  • Array of all zeros
  • Array with no zeros
  • Zeros clustered at the start
  • Zeros clustered at the end
  • Null array reference

Additional Considerations

The for loop touches each element exactly once, and the while loop runs for at most arr.length - writeIndex iterations, so the total work is O(n) where n is the array length. The implementation uses a constant number of extra integer variables (writeIndex and readIndex), so the auxiliary space is O(1).

Mutating the input array has a trade-off worth naming out loud. If the caller still needs the original ordering after the call, this approach destroys it. For callers that need to preserve the input, either clone the array before calling moveZeros or expose a variant that returns a new array.

For arrays that are mostly non-zero, a swap-based variant that only exchanges when writeIndex and readIndex diverge reduces the number of writes. On most data the difference is negligible, but on hardware with a tight memory bandwidth budget it can matter.

Interview Follow-up Questions

An interviewer will usually pivot from the base problem into one of the variations below once your first solution is working.

  • What if you also need to move all occurrences of a given value k to the end? – generalize the predicate from arr[readIndex] != 0 to arr[readIndex] != k; the compacting pass is otherwise identical.
  • What if the number of writes must be minimized? – use a two-pointer swap variant that only exchanges when writeIndex and readIndex diverge, so an already-compacted prefix costs zero writes.
  • What if non-zero elements must end up in reverse order? – sweep from right to left with writeIndex starting at arr.length - 1 and readIndex running down; the pattern mirrors.
  • What if the array is a billion-element dataset that does not fit in memory? – partition the input, run the two-pointer compaction on each chunk independently, then concatenate the compacted prefixes and pad with zeros. The in-chunk work is embarrassingly parallel.

Conclusion

Moving zeros to the end is deceptively simple: the real insight is that you compact the keepers rather than eject the throwaways. A single writeIndex pointer, one forward pass, and a short zero-fill loop give you an O(n) time and O(1) space solution that is easy to reason about and quick to code in an interview.

Try adapting this code to filter out any value, not just zero, and to sweep from right to left so the keepers end up at the back of the array. These small variations will prove to you that the technique is really about compaction, not zeros.

You can learn more about similar array problems here: Arrays on Daily Java Concept.