Mastering LeetCode Problem 0283
Problem 0283: Move Zeroes
Array manipulation problems on LeetCode often look deceptively simple — until you add constraints like:
- “Do it in-place”
- “Preserve the relative order of elements”
- “Use O(1) extra space”
LeetCode 283 – Move Zeroes is a perfect example. The task sounds straightforward:
Move all zeroes to the end of the array while keeping the relative order of the non-zero elements.
But designing a clean, efficient solution that works in-place and passes all edge cases is exactly what interviewers care about.
In this post, we’ll walk through:
- The problem statement in simple terms
- Why a naïve solution is not ideal
- The core idea: two-pointer in-place compaction
- A clean Python implementation
- Step-by-step walkthrough
- Time & space complexity
- Common interview mistakes and how to avoid them
- Key takeaways for real interviews
1. Problem Overview
You are given an array of integers nums. Your goal:
Move all
0s to the end of the array, without changing the relative order of the non-zero elements.
1.1. Constraints:
- You must modify the array in-place.
- You should aim for O(n) time and O(1) extra space.
- You must not return a new array — just update the input.
1.2. Example 1
Input: nums = [0, 1, 0, 3, 12]
Output: [1, 3, 12, 0, 0]
The non-zero elements [1, 3, 12] keep their relative order, and all zeroes are moved to the end.
1.3. Example 2
Input: nums = [0]
Output: [0]
Nothing to move; the array stays the same.
2. First Ideas: Why the Naïve Approach Isn’t Great
If there were no constraints about in-place modification or extra space, a very simple strategy might be:
- Build a new array with all non-zero elements.
- Count how many zeroes there were.
- Append that many zeroes to the end.
Something like:
non_zeros = [x for x in nums if x != 0]
zeros_count = len(nums) - len(non_zeros)
result = non_zeros + [0] * zeros_count
This works logically, but:
- It allocates extra memory proportional to
n(the array length). - It does not modify the input array in-place.
- In a real interview and on LeetCode, this usually fails the constraints.
We need something better: in-place, O(1) extra space, and still O(n) time.
3. Key Insight: In-Place Compaction with Two Pointers
To satisfy all constraints, we want to:
- Pull all non-zero elements to the front of the array.
- Maintain their relative order.
- Let all zeroes naturally “fall” to the back.
This is a classic application of the two-pointer pattern:
- One pointer scans the array.
- One pointer tracks where to write the next valid element.
3.1. Pointer Roles
Let’s define:
-
j– the scan pointer that iterates over every index from0ton - 1. -
i– the write pointer, indicating the position where the next non-zero element should go.
The algorithm:
- Initialize
i = 0. - For each
jfrom0tolen(nums) - 1:- If
nums[j]is non-zero:- Swap
nums[i]andnums[j]. - Increment
i(because we placed a non-zero element at positioni).
- Swap
- If
- When done, all non-zero elements are compacted at the beginning, and all zeroes are at the end.
3.2. Why This Preserves Order
Notice:
- We scan from left to right.
- The first non-zero we see goes to position
0. - The second non-zero goes to position
1. - And so on…
We never reorder non-zero elements relative to each other; we just move them forward, keeping their original sequence.
4. Python Implementation (In-Place Two-Pointer Solution)
Here’s a clean and interview-ready implementation in Python, based on the algorithm described above:
from typing import List
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Moves all zeros in the list to the end while preserving
the relative order of non-zero elements.
This function modifies nums in-place and returns None.
"""
i = 0 # index to place the next non-zero element
# j scans through the entire array
for j in range(len(nums)):
# When we find a non-zero, we want it at index i
if nums[j] != 0:
nums[i], nums[j] = nums[j], nums[i]
i += 1
This matches the logic of your solution, with the correct method signature for LeetCode (self included) and no unnecessary changes to the algorithm.
5. Step-by-Step Walkthrough
Let’s take the example:
nums = [0, 1, 0, 3, 12]
Initial:
i = 0-
jwill go from0to4
5.1. Iteration 1: j = 0
nums[j] = nums[0] = 0- It is zero → do nothing
-
istays0 - Array remains:
[0, 1, 0, 3, 12]
5.2. Iteration 2: j = 1
-
nums[1] = 1(non-zero) - Swap
nums[i]andnums[j]→ swapnums[0]andnums[1]
nums = [1, 0, 0, 3, 12]
i = 1
We’ve placed 1 at the front.
5.3. Iteration 3: j = 2
nums[2] = 0- It is zero → do nothing
-
iremains1 - Array:
[1, 0, 0, 3, 12]
5.4. Iteration 4: j = 3
-
nums[3] = 3(non-zero) - Swap
nums[i]andnums[j]→ swapnums[1]andnums[3]
nums = [1, 3, 0, 0, 12]
i = 2
Now 1 and 3 (first two non-zeros) are at positions 0 and 1.
5.5. Iteration 5: j = 4
-
nums[4] = 12(non-zero) - Swap
nums[i]andnums[j]→ swapnums[2]andnums[4]
nums = [1, 3, 12, 0, 0]
i = 3
Loop ends.
Final array:
[1, 3, 12, 0, 0]
All non-zero elements are at the beginning, preserving their original order [1, 3, 12].
All zeroes are at the end.
6. Edge Cases
A robust solution should handle edge cases gracefully. Let’s look at a few.
6.1. All Zeroes
nums = [0, 0, 0]
- The
if nums[j] != 0condition is never true. -
istays0. - The array stays
[0, 0, 0]— which is already correct.
6.2. No Zeroes
nums = [1, 2, 3]
- Every element is non-zero.
- For each
j, we swapnums[i]withnums[j], but sincei == jat each step:- We are effectively swapping an element with itself.
- The array stays
[1, 2, 3].
This shows the algorithm doesn’t break or do unnecessary complex work when the array is already “ideal”.
6.3. Zeroes Already at the End
nums = [1, 2, 3, 0, 0]
- The non-zero part is already at the front.
- The algorithm will perform only self-swaps for
j = 0, 1, 2, and ignore the zeros at3and4. - The final array remains
[1, 2, 3, 0, 0].
6.4. Single Element
nums = [0] -> [0]
nums = [5] -> [5]
Both are trivial but important to handle correctly. The loop runs once, and the logic still holds.
7. Time and Space Complexity
7.1. Time Complexity
We have:
- A single loop where
jgoes from0ton - 1, wheren = len(nums). - Each element is processed exactly once.
- Swaps are constant time.
So the overall time complexity is:
$\text{Time Complexity} = O(n)$
7.2. Space Complexity
We use only:
- Two integer variables (
iandj) in the process.
No additional arrays or data structures are created, so the extra space used is constant:
$\text{Space Complexity} = O(1)$
This exactly matches the in-place, O(1) space requirement in the problem.
8. Common Interview Mistakes
This problem is popular in interviews because it reveals how well you handle constraints and in-place logic. Here are some common pitfalls:
8.1. Creating a New Array
A typical incorrect approach:
non_zeros = [x for x in nums if x != 0]
zeros = [0] * (len(nums) - len(non_zeros))
nums = non_zeros + zeros
This may “work” in a vacuum, but:
- It does not modify the original
numsin-place. - It uses extra space proportional to
n. - On LeetCode and in interviews, this often fails the required constraints.
8.2. Using remove and append Repeatedly
Another inefficient pattern:
for x in nums:
if x == 0:
nums.remove(0)
nums.append(0)
Issues:
-
list.remove()is O(n) because it has to shift elements. - Combined with the loop, this often devolves into O(n²) time.
- It’s also error-prone due to changing the list while iterating over it.
8.3. Destroying the Relative Order
Some candidates try to:
- Count zeros
- Filter non-zeros
- Then write non-zeros in arbitrary order
If you don’t carefully maintain the original sequence of non-zero elements, the solution becomes incorrect, even if zeros are at the end.
8.4. Overcomplicating the Logic
Sometimes, solutions end up with:
- Multiple passes
- Many special cases
- Unnecessary conditions
The beauty of the two-pointer approach is that it’s both simple and optimal.
9. Interview Takeaways
LeetCode 283 – Move Zeroes is more than just an array cleanup problem. It’s a compact test of:
- Whether you think about in-place algorithms naturally
- How comfortable you are with the two-pointer pattern
- Whether you pay attention to constraints like preserving order and O(1) extra space
Patterns you reinforce here are directly applicable to other problems:
- Remove Element (LeetCode 27)
- Remove Duplicates from Sorted Array (LeetCode 26)
- General “filter in-place” or “compact array” tasks
- Partitioning arrays based on a condition
Once you internalize the idea:
“Use one pointer to scan, and another to write the next valid element.”
you’ll find that a whole family of array problems becomes much easier.