A two-pointer in-place solution for moving all zeroes to the end while preserving the relative order of non-zero elements.
Problem Restatement
We are given an integer array nums.
We need to move all zeroes to the end of the array while keeping the relative order of all non-zero elements unchanged.
The operation must be done in-place.
For example:
[0, 1, 0, 3, 12]should become:
[1, 3, 12, 0, 0]The order of:
1, 3, 12must stay the same.
The official statement requires an in-place solution and encourages minimizing the total number of operations. (leetcode.com)
Input and Output
| Item | Meaning |
|---|---|
| Input | Integer array nums |
| Output | Nothing |
| Mutation | Modify nums in-place |
| Requirement | Preserve relative order of non-zero values |
Function shape:
class Solution:
def moveZeroes(self, nums: list[int]) -> None:
...Examples
For:
nums = [0, 1, 0, 3, 12]The result should be:
[1, 3, 12, 0, 0]For:
nums = [0]The result stays:
[0]For:
nums = [1, 2, 3]There are no zeroes, so the array remains:
[1, 2, 3]First Thought: Build Another Array
A direct solution is:
- Collect all non-zero values.
- Count how many zeroes exist.
- Append that many zeroes at the end.
- Copy everything back.
Example:
[0, 1, 0, 3, 12]Non-zero values:
[1, 3, 12]Zero count:
2Final array:
[1, 3, 12, 0, 0]Code:
class Solution:
def moveZeroes(self, nums: list[int]) -> None:
values = []
for num in nums:
if num != 0:
values.append(num)
zeroes = len(nums) - len(values)
values.extend([0] * zeroes)
for i in range(len(nums)):
nums[i] = values[i]This works, but it uses extra memory.
The problem asks for an in-place solution.
Key Insight
We only need to track where the next non-zero value should go.
Use two pointers:
| Pointer | Meaning |
|---|---|
i | Current scanning position |
write | Position where the next non-zero value should be written |
As we scan left to right:
- If
nums[i]is non-zero, place it atnums[write]. - Then advance
write.
After all non-zero values are moved forward, every remaining position becomes zero.
Algorithm
Initialize:
write = 0First pass:
Scan the array from left to right.
Whenever we see a non-zero value:
nums[i] != 0write it into:
nums[write]Then increment write.
After this pass, all non-zero values appear at the front in the correct order.
Second pass:
Fill the remaining positions with zeroes.
while write < len(nums):
nums[write] = 0
write += 1Correctness
During the first pass, write always points to the first position where the next non-zero value should be placed.
Whenever the algorithm sees a non-zero value, it copies that value into nums[write] and increments write.
Because the scan proceeds from left to right, non-zero values are written in exactly the same order they originally appeared. Therefore relative order is preserved.
After the first pass:
- Positions before
writecontain all non-zero values in correct order. - Positions from
writeonward are irrelevant temporary values.
The second pass fills every remaining position with zero.
So after completion:
- All non-zero values appear first.
- Their original order is preserved.
- All remaining positions contain zero.
Therefore the final array satisfies the problem requirements.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(n) | We scan the array at most twice |
| Space | O(1) | No extra array proportional to input size |
Implementation
class Solution:
def moveZeroes(self, nums: list[int]) -> None:
write = 0
for i in range(len(nums)):
if nums[i] != 0:
nums[write] = nums[i]
write += 1
while write < len(nums):
nums[write] = 0
write += 1Code Explanation
We start with:
write = 0This is where the next non-zero value should go.
Then we scan every element:
for i in range(len(nums)):If the current value is non-zero:
if nums[i] != 0:we move it forward:
nums[write] = nums[i]and advance the write position:
write += 1After the loop, all non-zero values are packed at the front.
The remaining positions should become zero:
while write < len(nums):
nums[write] = 0
write += 1The function returns nothing because the array is modified in-place.
Alternative Swap Version
Another common implementation swaps elements immediately.
class Solution:
def moveZeroes(self, nums: list[int]) -> None:
write = 0
for i in range(len(nums)):
if nums[i] != 0:
nums[write], nums[i] = nums[i], nums[write]
write += 1This version may perform fewer writes in some cases.
Example:
[0, 1, 0, 3, 12]Process:
swap 1 with 0
swap 3 with 0
swap 12 with 0Result:
[1, 3, 12, 0, 0]Testing
def test_move_zeroes():
s = Solution()
nums = [0, 1, 0, 3, 12]
s.moveZeroes(nums)
assert nums == [1, 3, 12, 0, 0]
nums = [0]
s.moveZeroes(nums)
assert nums == [0]
nums = [1, 2, 3]
s.moveZeroes(nums)
assert nums == [1, 2, 3]
nums = [0, 0, 0]
s.moveZeroes(nums)
assert nums == [0, 0, 0]
nums = [4, 0, 5, 0, 0, 3]
s.moveZeroes(nums)
assert nums == [4, 5, 3, 0, 0, 0]
print("all tests passed")
test_move_zeroes()Test meaning:
| Test | Why |
|---|---|
[0, 1, 0, 3, 12] | Standard example |
[0] | Single zero |
[1, 2, 3] | No zeroes |
[0, 0, 0] | All zeroes |
[4, 0, 5, 0, 0, 3] | Mixed positions and multiple zeroes |