LeetCode 905 - Sort Array By Parity
The problem asks us to rearrange an array so that all even numbers appear before all odd numbers. The relative ordering among even numbers does not matter, and the relative ordering among odd numbers also does not matter.
Difficulty: 🟢 Easy
Topics: Array, Two Pointers, Sorting
Solution
Problem Understanding
The problem asks us to rearrange an array so that all even numbers appear before all odd numbers. The relative ordering among even numbers does not matter, and the relative ordering among odd numbers also does not matter. The problem explicitly states that any valid arrangement is acceptable.
We are given an integer array nums. Each element is a non negative integer. Our goal is to return the same array, or another array, where every even value comes first and every odd value comes afterward.
For example, if the input is:
[3,1,2,4]
then valid outputs include:
[2,4,3,1]
[4,2,1,3]
[2,4,1,3]
because all even numbers are grouped at the beginning and all odd numbers are grouped at the end.
The constraints are relatively small:
- The array length is between 1 and 5000
- Each number is between 0 and 5000
These limits tell us that even an O(n log n) sorting solution would work comfortably. However, the problem can actually be solved in linear time using a two pointer technique.
An important observation is that parity is determined by checking whether a number is divisible by 2:
- Even number:
num % 2 == 0 - Odd number:
num % 2 == 1
Several edge cases are worth considering:
- Arrays containing only even numbers
- Arrays containing only odd numbers
- Arrays with a single element
- Arrays where even and odd numbers alternate frequently
- Arrays containing zero, since
0is even
The problem guarantees that the input array is non empty, so we never need to handle a completely empty input.
Approaches
Brute Force Approach
A straightforward solution is to create two separate lists:
- One list for even numbers
- One list for odd numbers
We iterate through the array once. For each value:
- If it is even, append it to the even list
- Otherwise, append it to the odd list
At the end, concatenate the two lists and return the result.
This works because every even number is guaranteed to appear before every odd number in the final concatenated array.
Although this approach is already efficient enough for the constraints, it uses additional memory proportional to the size of the input.
Optimal Approach, Two Pointers
The key insight is that we do not actually need extra arrays. We can rearrange the elements in place.
We maintain two pointers:
- A left pointer that searches for misplaced odd numbers
- A right pointer that searches for misplaced even numbers
If the left pointer finds an odd number and the right pointer finds an even number, we swap them. This gradually pushes even numbers toward the front and odd numbers toward the back.
This approach works well because the problem does not require preserving the original order of elements. Since any valid arrangement is accepted, swapping is completely safe.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n) | O(n) | Uses separate lists for evens and odds |
| Optimal | O(n) | O(1) | Uses in place two pointer swapping |
Algorithm Walkthrough
Optimal Two Pointer Algorithm
- Initialize two pointers:
left = 0right = len(nums) - 1
The left pointer scans from the beginning, while the right pointer scans from the end.
2. Continue processing while left < right.
This ensures the pointers have not crossed.
3. Check whether the value at left is already even.
If it is even, it is already in the correct region of the array, so increment left.
4. Check whether the value at right is already odd.
If it is odd, it already belongs near the end, so decrement right.
5. If the left side contains an odd number and the right side contains an even number, swap them.
After the swap:
- The even number moves toward the front
- The odd number moves toward the back
- Move both pointers inward after swapping.
This avoids reprocessing elements that are now correctly positioned. 7. When the pointers cross, every even number will be before every odd number.
Why it works
The algorithm maintains an invariant during execution:
- All elements before
leftare even - All elements after
rightare odd
Whenever we find a misplaced odd number on the left and a misplaced even number on the right, swapping fixes both positions simultaneously. Since the pointers continuously move inward, every element is processed at most once, and the array eventually becomes partitioned into evens followed by odds.
Python Solution
from typing import List
class Solution:
def sortArrayByParity(self, nums: List[int]) -> List[int]:
left = 0
right = len(nums) - 1
while left < right:
if nums[left] % 2 == 0:
left += 1
elif nums[right] % 2 == 1:
right -= 1
else:
nums[left], nums[right] = nums[right], nums[left]
left += 1
right -= 1
return nums
The implementation starts by creating two pointers at opposite ends of the array.
Inside the loop, the algorithm first checks whether the left value is already even. If so, the left pointer advances because that element is correctly placed.
Next, it checks whether the right value is already odd. If so, the right pointer moves backward because that element is also correctly placed.
If neither condition is true, then:
- The left side contains an odd number
- The right side contains an even number
These elements are misplaced relative to the desired partition, so the algorithm swaps them.
After the swap, both pointers move inward because both newly placed elements are now correct.
Finally, the modified array is returned.
Go Solution
func sortArrayByParity(nums []int) []int {
left := 0
right := len(nums) - 1
for left < right {
if nums[left]%2 == 0 {
left++
} else if nums[right]%2 == 1 {
right--
} else {
nums[left], nums[right] = nums[right], nums[left]
left++
right--
}
}
return nums
}
The Go implementation follows the exact same logic as the Python version. Since slices in Go behave similarly to references to arrays, modifications happen in place automatically.
Go does not require special handling for integer overflow here because all values are very small. The algorithm also works correctly for slices of length 1 because the loop condition left < right immediately fails.
Worked Examples
Example 1
Input:
nums = [3,1,2,4]
Initial state:
| Left | Right | Array |
|---|---|---|
| 0 | 3 | [3,1,2,4] |
Step 1:
nums[left] = 3, oddnums[right] = 4, even- Swap them
| Left | Right | Array |
|---|---|---|
| 1 | 2 | [4,1,2,3] |
Step 2:
nums[left] = 1, oddnums[right] = 2, even- Swap them
| Left | Right | Array |
|---|---|---|
| 2 | 1 | [4,2,1,3] |
Now left > right, so the algorithm stops.
Final result:
[4,2,1,3]
This is valid because all even numbers appear before all odd numbers.
Example 2
Input:
nums = [0]
Initial state:
| Left | Right | Array |
|---|---|---|
| 0 | 0 | [0] |
Since left < right is false immediately, the loop never runs.
Final result:
[0]
The array already satisfies the condition because 0 is even.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Each pointer moves across the array at most once |
| Space | O(1) | Rearrangement happens in place |
The time complexity is linear because each element is processed at most once by either the left pointer or the right pointer. No nested iteration occurs.
The space complexity is constant because the algorithm only uses a few integer variables regardless of input size. No additional arrays or data structures are allocated.
Test Cases
def is_valid_parity_order(arr):
found_odd = False
for num in arr:
if num % 2 == 1:
found_odd = True
elif found_odd:
return False
return True
sol = Solution()
# Provided example
result = sol.sortArrayByParity([3, 1, 2, 4])
assert is_valid_parity_order(result) # mixed even and odd numbers
# Single even element
result = sol.sortArrayByParity([0])
assert result == [0] # smallest possible input
# All even numbers
result = sol.sortArrayByParity([2, 4, 6, 8])
assert is_valid_parity_order(result) # already valid
# All odd numbers
result = sol.sortArrayByParity([1, 3, 5, 7])
assert is_valid_parity_order(result) # no swaps needed
# Alternating parity
result = sol.sortArrayByParity([1, 2, 3, 4, 5, 6])
assert is_valid_parity_order(result) # frequent swapping
# Even numbers at the end
result = sol.sortArrayByParity([1, 3, 5, 2, 4, 6])
assert is_valid_parity_order(result) # many misplaced elements
# Even numbers at the beginning
result = sol.sortArrayByParity([2, 4, 6, 1, 3, 5])
assert is_valid_parity_order(result) # already partitioned
# Duplicate values
result = sol.sortArrayByParity([2, 2, 1, 1, 4, 4, 3, 3])
assert is_valid_parity_order(result) # repeated numbers
# Large values within constraints
result = sol.sortArrayByParity([5000, 4999, 4000, 3999])
assert is_valid_parity_order(result) # boundary numeric values
| Test | Why |
|---|---|
[3,1,2,4] |
Standard mixed parity example |
[0] |
Smallest valid input |
[2,4,6,8] |
All elements already even |
[1,3,5,7] |
All elements odd |
[1,2,3,4,5,6] |
Alternating parity pattern |
[1,3,5,2,4,6] |
Many misplaced values |
[2,4,6,1,3,5] |
Already partitioned correctly |
[2,2,1,1,4,4,3,3] |
Duplicate values |
[5000,4999,4000,3999] |
Constraint boundary values |
Edge Cases
Single Element Array
An array containing only one element can easily expose boundary condition bugs. For example, pointer based implementations may accidentally access invalid indices if loop conditions are incorrect.
This implementation handles the case safely because the loop only executes while left < right. For a single element array, both pointers start at index 0, so the loop never runs.
All Even or All Odd Arrays
Arrays where every element has the same parity can cause unnecessary swaps in poorly designed solutions.
For example:
[2,4,6,8]
or
[1,3,5,7]
In this implementation, the pointers simply move inward without performing swaps because every element is already correctly positioned relative to the partition.
Zero in the Input
Some implementations incorrectly treat zero as a special case. However, mathematically, zero is an even number because it is divisible by two.
This solution correctly classifies zero using:
num % 2 == 0
Therefore arrays such as:
[0,1,2]
are processed correctly, with zero placed among the even values at the front.