Given an integer array nums, move all 0's to the end of it while maintaining the relative order of the non-zero elements. You must do this in-place without making a copy of the array and aim to minimize the total number of operations (assignments).

Constraints:

  • 1 <= nums.length <= 10^4
  • -2^31 <= nums[i] <= 2^31 - 1

Example:

Input: nums = [0,1,0,3,12]
Output: [1,3,12,0,0]

Solution

Approach: Two Pointers (Two-Pass)

This problem can be efficiently solved using a two-pointer technique over two passes.

  • Initialize a write_pointer: This pointer will keep track of the position where the next non-zero element should be placed. Initialize it to 0.
  • First Pass (Move Non-Zeroes):
    • Iterate through the array with a read_pointer from the beginning to the end.
    • If nums[read_pointer] is not zero:
      • Place nums[read_pointer] at nums[write_pointer].
      • Increment write_pointer.
    • After this pass, all non-zero elements will be at the beginning of the array, in their original relative order. The write_pointer will be positioned at the first index where a zero should be placed.
  • Second Pass (Fill Remaining with Zeroes):
    • Iterate from the write_pointer to the end of the array.
    • Set each element nums[i] in this range to 0.

This approach ensures that the relative order of non-zero elements is preserved and minimizes the total number of assignments to N (where N is the length of the array), making it optimal for the specified constraints.

def moveZeroes(nums: list[int]) -> None:
    """
    Do not return anything, modify nums in-place instead.
    """
    write_pointer = 0
    
    # First pass: Move all non-zero elements to the beginning of the array
    for read_pointer in range(len(nums)):
        if nums[read_pointer] != 0:
            nums[write_pointer] = nums[read_pointer]
            write_pointer += 1
    
    # Second pass: Fill the rest of the array with zeros
    for i in range(write_pointer, len(nums)):
        nums[i] = 0