Move Zeroes in Java DSA: Brute Force, Extra Array, and the Two Pointer Trick
-
Last Updated: July 16, 2026
-
By: javahandson
-
Series
Learn Java in a easy way
Learn Move Zeroes in Java DSA with a beginner friendly guide. Compare brute force, an extra array, and the two pointer trick with a full dry run.
Move Zeroes in Java DSA is a small problem with a big lesson hiding inside. You have probably seen it on LeetCode and thought it was easy. The catch shows up when someone asks you to do it without a second array.
Here is the task in plain words. You get an array of numbers. Some of them are zeroes. Push every zero to the end, and keep the other numbers in their original order. One more rule matters most: change the array in place.
In the last article we used Kadane’s Algorithm to sweep an array once and track a running best. Now we sweep again, but this time we move values instead of sums. We start with brute force, try a second array, then land on the two pointer version that interviewers really want.
Let us pin down the rules first. Clear rules save you from silly bugs later.
For [0, 1, 0, 3, 12] the answer is [1, 3, 12, 0, 0]. Notice that 1 still comes before 3, and 3 still comes before 12. That ordering rule is the whole difficulty. Without it, you could just swap zeroes with the last element and finish in seconds.
| 💡 Interview Insight Many people miss the words in place. If you build a fresh array and return it, you solved a different problem. Say the phrase out loud during the interview so they know you caught it. |
Two small ideas carry this whole problem. Both come back in many array questions, so learn them once and reuse them forever.
In place means you work inside the array you already have. You do not allocate a second array of the same size. Your extra memory stays constant, no matter how big the input grows. So you write values over old slots instead of copying them somewhere new.
Here two indexes walk the same array with different jobs. One index reads every value, front to back. The other index marks the slot where the next non-zero number belongs. Because the writer moves slower than the reader, it never overwrites a value you still need.
The first idea most beginners reach for feels natural. Find a zero, then slide everything after it one step left, and drop a zero at the end. Repeat until no zero sits out of place.
count = 0 // zeroes already parked at the end
i = 0
while i < n - count:
if nums[i] == 0:
shift every element after i one step left
nums[n-1] = 0
count = count + 1 // one more zero parked
else:
i = i + 1 // only move on when no shift happenedThe count variable tracks how many zeroes already sit at the back. We stop before them, otherwise we would shift the same zeroes forever. Also notice that we do not move i after a shift, because a brand new value just landed at that index.
public class MoveZeroesBrute {
public static void moveZeroes(int[] nums) {
int n = nums.length;
int count = 0;
int i = 0;
while (i < n - count) {
if (nums[i] == 0) {
for (int j = i; j < n - 1; j++) {
nums[j] = nums[j + 1];
}
nums[n - 1] = 0;
count++;
} else {
i++;
}
}
}
public static void main(String[] args) {
int[] nums = { 0, 1, 0, 3, 12 };
moveZeroes(nums);
System.out.println(java.util.Arrays.toString(nums)); // [1, 3, 12, 0, 0]
}
}Let us walk the code slowly. Every line has one clear job.
This solution edits the array in place, so it respects the main rule. Sadly it does a lot of repeated work, which we fix next.
Let us trace nums = [0, 1, 0, 3, 12] by hand. First, here is what each column means, so nothing feels cryptic.
Watch two things as you read. The i column only moves forward when no shift happened. The count column grows by one every time we park a zero.
| Step | i (read index) | nums[i] (value there) | Action | Array after | count (zeroes parked) |
|---|---|---|---|---|---|
| 1 | 0 | 0 | Shift left, park a zero at the end | [1, 0, 3, 12, 0] | 1 |
| 2 | 0 | 1 | Not zero, so move i forward | [1, 0, 3, 12, 0] | 1 |
| 3 | 1 | 0 | Shift left, park a zero at the end | [1, 3, 12, 0, 0] | 2 |
| 4 | 1 | 3 | Not zero, so move i forward | [1, 3, 12, 0, 0] | 2 |
| 5 | 2 | 12 | Not zero, so move i forward | [1, 3, 12, 0, 0] | 2 |
Look at step one and step two together. After the shift, i stayed at 0 on purpose. A fresh value, the 1, had just slid into that slot, so we had to inspect it before moving on.
Now check why the loop stops after step five. Here n is 5 and count is 2, so the guard reads i < 3. Since i has climbed to 3, the loop ends. We never look at index 3 or 4 again, because both hold zeroes we parked ourselves.
Count the work too. Two zeroes meant two full shifts of the tail. On a big array with many zeroes, that repeated dragging is exactly what makes this approach crawl.
Each zero can trigger a full shift of the array. With many zeroes, that becomes n shifts of n elements. So the time lands at O(n squared). The space stays O(1), since we only keep a few counters. For a tiny array nobody notices. For a large one it crawls.
Here is a cleaner idea. Walk the array once and copy every non-zero value into a fresh array. Then fill whatever space is left with zeroes. Finally copy the result back into the original array.
temp = new array of size n // starts full of zeroes in Java
k = 0
for each num in nums:
if num != 0:
temp[k] = num
k = k + 1
copy temp back into numspublic class MoveZeroesExtraArray {
public static void moveZeroes(int[] nums) {
int[] temp = new int[nums.length];
int k = 0;
for (int num : nums) {
if (num != 0) {
temp[k] = num;
k++;
}
}
for (int i = 0; i < nums.length; i++) {
nums[i] = temp[i];
}
}
public static void main(String[] args) {
int[] nums = { 0, 1, 0, 3, 12 };
moveZeroes(nums);
System.out.println(java.util.Arrays.toString(nums)); // [1, 3, 12, 0, 0]
}
}Time drops to O(n), which is a real win. But space climbs to O(n), and the whole point of the problem was to avoid that. An interviewer will push back here almost every time.
This trace is the easiest of the three. We read nums from left to right and only care about the non-zero values. Remember that Java hands us temp already filled with zeroes.
| Step | Value read | Zero? | Action | temp after | k (next free slot) |
|---|---|---|---|---|---|
| 1 | 0 | Yes | Skip it, write nothing | [0, 0, 0, 0, 0] | 0 |
| 2 | 1 | No | Copy 1 into temp[0] | [1, 0, 0, 0, 0] | 1 |
| 3 | 0 | Yes | Skip it, write nothing | [1, 0, 0, 0, 0] | 1 |
| 4 | 3 | No | Copy 3 into temp[1] | [1, 3, 0, 0, 0] | 2 |
| 5 | 12 | No | Copy 12 into temp[2] | [1, 3, 12, 0, 0] | 3 |
Notice that we never wrote a single zero ourselves. The zeroes at index 3 and index 4 were already there when Java built the array. That free head start is what makes this version so short.
The last step copies temp back into nums, so the caller sees [1, 3, 12, 0, 0]. Compare this trace with the brute force one above. Same answer, five steps instead of five plus two full shifts, but we paid for a whole second array to get it.
| 💡 Interview Insight Mentioning this approach still earns points. It shows you can reach O(n) time quickly. Just say the space cost out loud yourself, then offer to remove it. Naming your own trade-off reads as confidence, not weakness. |
Now for the version interviewers want. The trick is to stop thinking about moving zeroes at all. Instead, think about pulling the non-zero values forward. The zeroes then sort themselves out.
Keep one index called insert. It marks the slot where the next non-zero value belongs. Walk the array with a second index. Every time you meet a non-zero number, write it at insert and bump insert forward. When the walk ends, every slot from insert onward gets a zero.
insert = 0
for each num in nums:
if num != 0:
nums[insert] = num
insert = insert + 1
for i from insert to n-1:
nums[i] = 0public class MoveZeroesTwoPointer {
public static void moveZeroes(int[] nums) {
int insert = 0;
for (int num : nums) {
if (num != 0) {
nums[insert] = num;
insert++;
}
}
while (insert < nums.length) {
nums[insert] = 0;
insert++;
}
}
public static void main(String[] args) {
int[] nums = { 0, 1, 0, 3, 12 };
moveZeroes(nums);
System.out.println(java.util.Arrays.toString(nums)); // [1, 3, 12, 0, 0]
}
}This version does the most thinking, so let us break it apart gently.
Here is the part that trips people up. Does the write at nums[insert] destroy a value we still need? It cannot. The insert index never runs ahead of the reader, so any slot we overwrite was already read.
| 💡 Interview Insight A common follow-up asks why we do not swap instead of overwrite. Swapping also works and keeps the same Big-O. Overwriting is simpler to explain, though swapping does fewer total writes when zeroes are rare. Either answer is fine if you can defend it. |
We traced the other two approaches already, so let us give this one the same treatment. Follow the same input, nums = [0, 1, 0, 3, 12]. Watch how insert crawls forward while the reader races ahead.
| Step | num read | Zero? | Action | Array after | insert (next slot) |
|---|---|---|---|---|---|
| 1 | 0 | Yes | Skip it, write nothing | [0, 1, 0, 3, 12] | 0 |
| 2 | 1 | No | Write 1 at index 0 | [1, 1, 0, 3, 12] | 1 |
| 3 | 0 | Yes | Skip it, write nothing | [1, 1, 0, 3, 12] | 1 |
| 4 | 3 | No | Write 3 at index 1 | [1, 3, 0, 3, 12] | 2 |
| 5 | 12 | No | Write 12 at index 2 | [1, 3, 12, 3, 12] | 3 |
Look at step five closely. The array now reads [1, 3, 12, 3, 12], which looks wrong at a glance. Do not panic. The values at index 3 and index 4 are leftover junk, and the reader has already passed them. Since insert is 3, the second loop overwrites both with zeroes.
| Fill step | Index | Action | Array after |
|---|---|---|---|
| 1 | 3 | Write a zero | [1, 3, 12, 0, 12] |
| 2 | 4 | Write a zero | [1, 3, 12, 0, 0] |
The final array is [1, 3, 12, 0, 0]. Every zero sits at the back, and 1, 3, and 12 kept their original order. We touched each slot at most twice, and we never built a second array.

The sketch above shows the same trace. Phase one pulls the non-zero values forward, and the shaded cells show the part of the array that is already settled. Phase two fills the tail with zeroes. Many people find this picture easier than the table, so use whichever clicks for you.
All three approaches give the right answer. They just pay different prices. Here is the plain comparison.
| Approach | Time | Space | Note |
|---|---|---|---|
| Brute force shifting | O(n squared) | O(1) | In place, but slow when zeroes are common |
| Extra array | O(n) | O(n) | Fast and simple, breaks the in-place rule |
| Two pointer | O(n) | O(1) | Fast and in place, the answer they want |
In an interview, walk this exact path. Start with brute force to prove you understand the problem. Bring up the extra array to show you can hit O(n) time. Then remove the extra array with two pointers. That climb from slow to clean is what interviewers grade you on.
A few small traps catch beginners on this problem. Keep these in mind.
A: You get an array of integers and must push every zero to the end while keeping the non-zero numbers in their original relative order. The array must be changed in place, so you return nothing.
A: The two pointer approach is best. Keep an insert index for the next non-zero slot, walk the array once writing every non-zero value at insert, then fill the rest with zeroes. That gives O(n) time and O(1) extra space.
A: Sorting reorders the non-zero numbers too, which breaks the main rule. The problem requires that values like 1, 3, and 12 keep their original relative order.
A: No. The insert index never runs ahead of the reading index, so any slot you overwrite was already read. Leftover junk in the tail gets replaced with zeroes in the second loop.
A: Brute force shifting runs in O(n squared) time with O(1) space. The extra array approach runs in O(n) time but uses O(n) space. The two pointer approach runs in O(n) time with O(1) space.
A: Both work and share the same Big-O. Overwriting is easier to explain, while swapping does fewer total writes when zeroes are rare. Either is fine in an interview if you can defend the choice.
Move Zeroes in Java looks like a beginner warm-up, and in a way it is. But the two pointer idea inside it is a real skill. You will reuse that slow-writer and fast-reader pattern in Remove Duplicates, Remove Element, and many string problems.
So take the pattern, not just the answer. Try brute force first when you feel stuck. Reach for an extra array when you want speed fast. Then squeeze out the extra memory with two pointers. Once that move feels natural, a whole family of array problems opens up for you.
You now have Move Zeroes in your toolkit, so let us keep the momentum going. Here is where you came from and where you head next in this series.