283. Move Zeroes - Explanation
Description
You are given an integer array nums, move all 0's to the end of it while maintaining the relative order of the non-zero elements.
Note that you must do this in-place without making a copy of the array.
Example 1:
Input: nums = [0,0,1,2,0,5]
Output: [1,2,5,0,0,0]Example 2:
Input: nums = [0,1,0]
Output: [1,0,0]Constraints:
1 <= nums.length <= 10,000-(2^31) <= nums[i] <= ((2^31)-1)
Follow up: Could you minimize the total number of operations done?
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- Arrays - Understanding array indexing and in-place modification
- Two Pointers - Using two pointers to partition or rearrange elements within an array
1. Extra Space
Intuition
The simplest approach is to separate non-zero elements from zeros using extra storage. We collect all non-zero elements first, then write them back to the original array, filling the remaining positions with zeros. This guarantees the relative order of non-zero elements is preserved.
Algorithm
- Create a temporary list
tmpand add all non-zero elements fromnums. - Iterate through the original array:
- For indices less than
tmp.length, copy the value fromtmp. - For remaining indices, set the value to
0.
- For indices less than
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
tmp = []
for num in nums:
if num != 0:
tmp.append(num)
for i in range(len(nums)):
if i < len(tmp):
nums[i] = tmp[i]
else:
nums[i] = 0Time & Space Complexity
- Time complexity:
- Space complexity:
2. Two Pointers (Two Pass)
Intuition
We can avoid extra space by overwriting the array in place. Use a left pointer to track where the next non-zero element should go. As we scan with a right pointer, each non-zero element gets copied to the left pointer's position. After the first pass, all non-zero elements are at the front in order. A second pass fills the remaining positions with zeros.
Algorithm
- Initialize
l = 0to track the position for the next non-zero element. - First pass: Iterate through the array with pointer
r. For each non-zero element, copy it tonums[l]and incrementl. - Second pass: Fill positions from
lto the end with0.
Time & Space Complexity
- Time complexity:
- Space complexity:
3. Two Pointers (One Pass)
Intuition
Instead of copying values and then filling zeros separately, we can swap elements in a single pass. The left pointer marks the boundary between processed non-zero elements and unprocessed elements. When we encounter a non-zero element with the right pointer, we swap it with the element at the left pointer. This naturally pushes 0 to the right while keeping non-zero elements in their relative order.
Algorithm
- Initialize
l = 0to track the swap position. - Iterate through the array with pointer
r. For each non-zero element:- Swap
nums[l]andnums[r]. - Increment
l.
- Swap
- After the loop, all non-zero elements are at the front and zeros are at the end.
Time & Space Complexity
- Time complexity:
- Space complexity:
Common Pitfalls
Swapping When Pointers Are at the Same Position
When both pointers l and r point to the same non-zero element, swapping is unnecessary and wastes operations. While this does not affect correctness, adding a check like if (l != r) before swapping improves efficiency and avoids redundant writes, which can matter for performance-sensitive applications.
Disrupting Relative Order of Non-Zero Elements
The problem requires maintaining the relative order of non-zero elements. Some approaches incorrectly swap non-zero elements with each other or move them out of sequence. The two-pointer technique works because the left pointer only advances when a non-zero element is placed, ensuring all non-zero elements shift left in their original order while zeros naturally accumulate at the end.
Sign in to join the discussion