Category: Medium | Concepts used: Two-pointer technique, in-place array modification
Problem Statement
Given an array, move all 0s to the end, while keeping the relative order of the non-zero elements unchanged. Must be done in-place (modifying the original array).
Input : [0, 1, 0, 3, 12] Output: [1, 3, 12, 0, 0]
Examples (with edge scenarios)
| # | Input | Output | Why |
|---|---|---|---|
| 1 | [0, 1, 0, 3, 12] | [1, 3, 12, 0, 0] | Non-zero elements keep their relative order |
| 2 | [] (empty) | [] | Nothing to move |
| 3 | [0, 0, 0] | [0, 0, 0] | All zeroes — nothing changes visibly |
| 4 | [1, 2, 3] (no zeroes) | [1, 2, 3] | Unchanged — nothing to move |
| 5 | [4, 0, 0, 5, 0, 6] | [4, 5, 6, 0, 0, 0] | Multiple zeroes scattered throughout |
Common Fresher Mistake
Mistake What happens Fix Creating a brand-new array to build the result Works, but violates the “in-place” requirement often specified for this exact problem Use a two-pointer swap technique on the original array instead Simply removing zeroes and appending them at the end using extra lists Also works, but again typically not what’s asked when “in-place” is specified Practice the swap-based two-pointer version, since that’s what’s usually expected
Before You Code: Clarify the Contract
Before choosing an algorithm, confirm whether the array may be null or empty, whether duplicates and original order matter, whether the method may modify the input, and whether the answer should contain values or original indices. These choices can change both the code and the best data structure.
Analogy: Conveyor Belt Sorter
Imagine you are at a recycling center with a conveyor belt of mixed items (numbers) and empty spaces (zeroes):
- You have a pointer called
insertPosthat marks the beginning of the belt where clean items should be placed. - A scanner pointer
imoves along the belt checking each slot:- When scanner
ifinds a real item (non-zero), you swap it into the slot pointed to byinsertPos. - You then step the
insertPospointer forward by1(insertPos++), ready for the next item. - If scanner
ifinds an empty space (zero), you do nothing and just moveiforward.
- When scanner
- Since you are constantly swapping real items forward, all the empty spaces (zeroes) naturally get pushed back to the tail of the conveyor belt!
Solution 1 — Using an Extra Array (Simple, But Not In-Place)
Intuition
Walk through the array once, copying every non-zero value into a new result array as we go. Once all non-zero values are placed, fill whatever’s left over at the end with zeroes — this naturally groups all the zeroes at the tail.
import java.util.Arrays;
public class MoveZeroesExtraArray {
public static int[] moveZeroes(int[] arr) {
int[] result = new int[arr.length];
int index = 0;
// first, copy all non-zero values in order
for (int num : arr) {
if (num != 0) {
result[index++] = num;
}
}
// remaining slots are already 0 by default in Java (int arrays init to 0)
return result;
}
public static void main(String[] args) {
System.out.println(Arrays.toString(moveZeroes(new int[]{0, 1, 0, 3, 12}))); // [1, 3, 12, 0, 0]
}
}
Output:
[1, 3, 12, 0, 0]
Interviewer’s take
This is correct and easy to explain, but it uses O(n) extra space and creates a whole new array — which usually isn’t what’s wanted when the question specifically says “in-place.” Good to mention as the “obvious first idea” but be ready to convert to Solution 2.
Follow-up questions you might get:
- “Can you do this without creating a new array?” → leads to Solution 2.
Solution 2 — Two-Pointer, In-Place Swap (Recommended)
Intuition
Keep a pointer (insertPos) marking “the next spot where a non-zero value should go.” Walk through the array with a second pointer; every time you find a non-zero value, swap it into the insertPos slot and advance insertPos. Since we only ever swap non-zero values forward into earlier positions, all the zeroes naturally get pushed toward the back, in the same relative order they were passed over.
import java.util.Arrays;
public class MoveZeroesInPlace {
public static void moveZeroes(int[] arr) {
int insertPos = 0; // where the next non-zero value should go
for (int i = 0; i < arr.length; i++) {
if (arr[i] != 0) {
// swap arr[i] and arr[insertPos]
int temp = arr[insertPos];
arr[insertPos] = arr[i];
arr[i] = temp;
insertPos++;
}
}
}
public static void main(String[] args) {
int[] arr = {0, 1, 0, 3, 12};
moveZeroes(arr);
System.out.println(Arrays.toString(arr)); // [1, 3, 12, 0, 0]
int[] arr2 = {4, 0, 0, 5, 0, 6};
moveZeroes(arr2);
System.out.println(Arrays.toString(arr2)); // [4, 5, 6, 0, 0, 0]
}
}
Output:
[1, 3, 12, 0, 0]
[4, 5, 6, 0, 0, 0]
Dry Run (arr = [0, 1, 0, 3, 12])
insertPos=0
i=0: arr[0]=0 -> skip (it's zero)
i=1: arr[1]=1 -> non-zero -> swap arr[0] and arr[1] -> arr=[1,0,0,3,12], insertPos=1
i=2: arr[2]=0 -> skip
i=3: arr[3]=3 -> non-zero -> swap arr[1] and arr[3] -> arr=[1,3,0,0,12], insertPos=2
i=4: arr[4]=12 -> non-zero -> swap arr[2] and arr[4] -> arr=[1,3,12,0,0], insertPos=3
Final: [1, 3, 12, 0, 0]
Interviewer’s take
This is the preferred final answer — O(n) time, O(1) extra space (truly in-place), and preserves relative order of non-zero elements correctly through the swap mechanism. This two-pointer “partition” style pattern (one pointer scanning, one pointer marking where to place the next “good” element) is reused across many similar array-rearrangement problems.
Follow-up questions you might get:
- “Why does swapping (not just overwriting) preserve correctness?” → Overwriting without swapping would lose the value that was originally at
insertPos(unless it happened to be zero); swapping ensures we never lose track of any value — we just relocate it. - “What’s the minimum number of swaps performed?” → Exactly as many swaps as there are non-zero elements — no wasted work.
📊 Visual Flowchart
graph TD
Start["Input Array arr"] --> Init["insertPos = 0"]
Init --> Loop{"i < arr.length?"}
Loop -->|Yes| CheckZero{"arr[i] != 0?"}
CheckZero -->|Yes| Swap["swap arr[i] and arr[insertPos]"]
Swap --> AdvanceIP["insertPos++"]
CheckZero -->|No| Next["i++"]
AdvanceIP --> Next
Next --> Loop
Loop -->|No| End["Return (Modified arr)"]
Final Verdict — Which Solution Should You Give?
Solution 1 (extra array) ──O(n) space, not truly in-place──► Okay as a starting idea
Solution 2 (two-pointer swap) ──O(1) space, in-place────────► PREFERRED FINAL ANSWER
- Solution 2 is the expected final answer, especially since “in-place” is a standard requirement for this classic problem.
Quick Recap
| Approach | Time | Space | In-place? |
|---|---|---|---|
| Extra array | O(n) | O(n) | No |
| Two-pointer swap | O(n) | O(1) | Yes |
- Author
- TechByteByByte Editorial Team
- Reviewed by
- TechByteByByte Admin
- Published
- Last reviewed