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:
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:
- Initialize
writeIndexto0. This is the next slot that will receive a non-zero element. - Iterate
readIndexfrom0toarr.length - 1. - If
arr[readIndex]is non-zero, copy it intoarr[writeIndex]and incrementwriteIndex. - If
arr[readIndex]is zero, do nothing and letreadIndexadvance on its own. - After the first pass, every non-zero has been packed into
arr[0..writeIndex-1]in input order. - Fill the remaining slots
arr[writeIndex..length-1]with0to complete the result.
Java Program to Move Zeros to the End
Here is the full, compilable Java program that applies the two-pointer approach:
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 step | writeIndex (after) |
|---|---|---|---|---|---|
| 0 | 4 | yes | 0 | {4, 0, 1, 0, 2} | 1 |
| 1 | 0 | no | 1 | {4, 0, 1, 0, 2} | 1 |
| 2 | 1 | yes | 1 | {4, 1, 1, 0, 2} | 2 |
| 3 | 0 | no | 2 | {4, 1, 1, 0, 2} | 2 |
| 4 | 2 | yes | 2 | {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.
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.
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.
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 runningwriteIndexinstead 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 theforloop with awhileloop that overwritesarr[writeIndex..length-1]with0. - Decrementing readIndex after a shift – once
readIndexrevisits a slot it just wrote to, the loop never terminates on inputs with trailing zeros. AdvancereadIndexunconditionally in theforheader and letwriteIndexmove independently. - Initializing writeIndex to 1 – shifts the entire packed region right by one, so the earliest non-zero never reaches index
0. StartwriteIndexat0so the first non-zero lands atarr[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
kto the end? – generalize the predicate fromarr[readIndex] != 0toarr[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
writeIndexandreadIndexdiverge, 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
writeIndexstarting atarr.length - 1andreadIndexrunning 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.



