21. Move all zero values to the end of an array while preserving the order of nonzero values.
Modify or return the array with stable nonzero ordering and analyze complexity.
I use a write pointer to track the next position for a nonzero value. I scan the array from left to right. When I find a nonzero value, I copy it to the write position and move the pointer forward. After the scan, I fill every remaining position with zero. This keeps the nonzero values in their original order. The time complexity is O(n), and the auxiliary space complexity is O(1).
See the Code while reading this explanation.
The task is to move every zero to the end of the array while keeping the nonzero values in the same relative order. I use an in-place write-pointer method. A read index checks each value. The write pointer marks where the next nonzero value should go. After all nonzero values are placed, the remaining positions are filled with zeros.
- What input sizes, value ranges, and edge cases should the solution handle?
- What output should be returned for empty, invalid, or duplicate input?
- Should I prioritize execution time or memory use, and may I use the standard library?
The input is an array of integers. In the example, the input is [0, 1, 0, 3, 12, 0, 5, 0].
The required output is [1, 3, 12, 5, 0, 0, 0, 0].
The nonzero values must remain in their original relative order. The array is modified in place, and the function returns that modified array.
I use a variable named write. It stores the index where the next nonzero value should be placed.
The central invariant is that every position before write contains the nonzero values seen so far, in their original order.
This method fits the problem because it preserves order and uses only constant extra memory.
I start with write = 0.
This means that no nonzero values have been placed yet. The first nonzero value should be written at index 0.
I then scan the array from left to right using index i.
Start with nums = [0, 1, 0, 3, 12, 0, 5, 0] and write = 0.
At i = 0, nums[i] is 0. I skip it. The array stays [0, 1, 0, 3, 12, 0, 5, 0], and write stays 0.
At i = 1, nums[i] is 1. It is nonzero. I write 1 at nums[0]. The array becomes [1, 1, 0, 3, 12, 0, 5, 0]. Then write becomes 1.
At i = 2, nums[i] is 0. I skip it. The array stays [1, 1, 0, 3, 12, 0, 5, 0], and write stays 1.
At i = 3, nums[i] is 3. It is nonzero. I write 3 at nums[1]. The array becomes [1, 3, 0, 3, 12, 0, 5, 0]. Then write becomes 2.
At i = 4, nums[i] is 12. It is nonzero. I write 12 at nums[2]. The array becomes [1, 3, 12, 3, 12, 0, 5, 0]. Then write becomes 3.
At i = 5, nums[i] is 0. I skip it. The array stays [1, 3, 12, 3, 12, 0, 5, 0], and write stays 3.
At i = 6, nums[i] is 5. It is nonzero. I write 5 at nums[3]. The array becomes [1, 3, 12, 5, 12, 0, 5, 0]. Then write becomes 4.
At i = 7, nums[i] is 0. I skip it. The array stays [1, 3, 12, 5, 12, 0, 5, 0], and write stays 4.
The first scan is complete. I fill indices 4 through 7 with zero. The final array is [1, 3, 12, 5, 0, 0, 0, 0].
Every nonzero value is copied in the same order in which it appears in the input.
The write pointer always marks the next free position for a nonzero value. Therefore, the positions before write contain exactly the nonzero values seen so far, in stable order.
After the scan, every remaining position is filled with zero. This places all zeros at the end without changing the relative order of the nonzero values.
The first loop reads each element. When the current value is nonzero, the code copies it to nums[write] and increases write.
The second loop starts at write and sets every remaining position to zero.
Finally, the function returns the modified array.
The first loop scans n elements. The second loop fills at most n remaining positions. The total time complexity is O(n).
The algorithm uses only a few variables, so the auxiliary space complexity is O(1).
Important edge cases include an array containing only zeros, an array containing no zeros, a single zero, a single nonzero value, and zeros already grouped at the end.
The key idea is to separate reading from writing. The read index examines every element from left to right. The write index marks the next position where a nonzero value belongs. When a nonzero value is found, it is copied to nums[write], and write moves forward. The invariant is that every position before write contains all nonzero values seen so far in their original order. After the scan, every position from write to the end is set to zero. This preserves stable nonzero ordering and modifies the array in place.
from typing import List
def move_zeros_to_end(nums: List[int]) -> List[int]:
"""Move all zeros to the end while preserving nonzero order."""
# write is the next position where a nonzero value should be placed.
write = 0
# Store the array length for both passes.
n = len(nums)
# First pass: copy every nonzero value toward the front.
for i in range(n):
# Zero values are skipped during this pass.
if nums[i] != 0:
# Place the current nonzero value at the next write position.
nums[write] = nums[i]
# Move to the next available write position.
write += 1
# Second pass: fill all remaining positions with zero.
for i in range(write, n):
nums[i] = 0
# Return the same array after modifying it in place.
return nums
if __name__ == "__main__":
# Example from the diagram.
example = [0, 1, 0, 3, 12, 0, 5, 0]
# Run the function and display the result.
result = move_zeros_to_end(example)
print(result)
# Expected output:
# [1, 3, 12, 5, 0, 0, 0, 0]Let n be the length of the array. The algorithm takes O(n) time. The first loop reads each array element once. The second loop fills the remaining positions with zeros. Together, the work is still linear. The auxiliary space is O(1). Auxiliary space means extra memory used by the algorithm. Only the write index, loop variables, and the array length are stored. No extra array grows with the input.
This pattern is useful when data must be compacted in place. Examples include moving empty entries to the end of a buffer, keeping valid records before invalid records, and removing gaps while preserving the order of the remaining items.
The interviewer is checking whether the candidate recognizes an in-place array compaction pattern. The problem tests pointer management, stable ordering, safe overwriting, and clear reasoning about changing state. It also shows whether the candidate can divide the work into two simple phases: place the nonzero values first, then fill the remaining positions with zeros. The interviewer also expects correct O(n) time and O(1) auxiliary space analysis.
A common mistake is swapping zeros with later values in a way that changes the relative order of the nonzero values. Another mistake is increasing write when the current value is zero. Some candidates forget the second loop, so old values remain after the nonzero section. Another mistake is creating a second array and then claiming O(1) auxiliary space. Candidates may also use pointer updates in the wrong order and overwrite a value before it has been read.
State the invariant before coding: every index before write already contains the nonzero values seen so far in their original order.









