A clear explanation of checking whether an array can be reordered into pairs where one number is double the other.
Problem Restatement
We are given an integer array arr.
We want to reorder the array so that for every pair:
(arr[2 * i], arr[2 * i + 1])the second number equals double the first number:
arr[2 * i + 1] = 2 * arr[2 * i]Return true if such a reordering is possible. Otherwise, return false.
The official constraints are:
| Constraint | Value |
|---|---|
2 <= arr.length <= 3 * 10^4 | Array size |
arr.length is even | Pairing is possible in principle |
-10^5 <= arr[i] <= 10^5 | Integer range |
Source: LeetCode problem statement. (leetcode.com)
Input and Output
| Item | Meaning |
|---|---|
| Input | Integer array arr |
| Output | true if valid doubled pairing exists, otherwise false |
| Pair rule | One value must equal double the other |
Example function shape:
def canReorderDoubled(arr: list[int]) -> bool:
...Examples
Example 1:
arr = [3, 1, 3, 6]Output:
FalseExplanation:
We can pair:
3 -> 6But then:
1
3remain unmatched.
So a valid reordering does not exist.
Example 2:
arr = [2, 1, 2, 6]Output:
FalseExplanation:
The number 1 requires a matching 2.
But there are two 2s and one 6.
Eventually one 2 cannot find a matching 4.
Example 3:
arr = [4, -2, 2, -4]Output:
TrueOne valid pairing is:
-2 -> -4
2 -> 4Example 4:
arr = [0, 0]Output:
TrueBecause:
0 = 2 * 0the two zeros form a valid pair.
First Thought: Greedy Pairing
A natural idea is:
- Pick a number
x - Search for
2 * x - Remove both numbers
- Continue
We can track counts using a frequency map.
But ordering matters.
Consider:
arr = [2, 4, 8]If we process 8 first, we search for 16 and fail immediately.
But the real issue is that 8 should have been used as the double of 4.
So we must process numbers carefully.
Key Insight
The important observation is:
Small absolute values must be matched before larger absolute values.
Example:
1 -> 2 -> 4 -> 8The number 2 may be needed both as:
- double of
1 - base for
4
If we use it incorrectly, later matches fail.
To avoid this, process numbers in increasing order of absolute value.
That guarantees smaller numbers claim their doubles first.
Why Absolute Value Matters
Negative numbers create a subtle issue.
Example:
-2 -> -4Notice:
|-2| < |-4|If we sort normally:
[-4, -2]then we process -4 first and incorrectly search for -8.
Sorting by absolute value fixes this:
[-2, -4]Now pairing works correctly.
Algorithm
- Count frequencies using
Counter. - Sort numbers by absolute value.
- For each number
x:- If
count[x] == 0, skip it. - If
count[2 * x] == 0, returnFalse. - Otherwise:
- use one
x - use one
2 * x
- use one
- If
- If all numbers are matched successfully, return
True.
Handling Zero
Zero is special because:
0 * 2 = 0So zeros must pair with other zeros.
That means the count of zero must be even.
The algorithm handles this naturally.
Example:
count[0] = 4Processing zeros repeatedly removes them in pairs.
But:
count[0] = 3would eventually fail because one zero remains unmatched.
Correctness
We process numbers in increasing order of absolute value.
Consider a number x.
At the moment we process x, every smaller absolute value has already formed its required pairs. Therefore, if x is still available, it must now find a matching 2 * x.
If no copy of 2 * x exists, then no valid pairing is possible. Any valid arrangement requires every remaining x to be paired with one remaining 2 * x.
If a copy of 2 * x exists, we consume one copy of each. This correctly creates one valid pair.
Sorting by absolute value is necessary because larger numbers may themselves be doubles needed by smaller numbers. Processing smaller absolute values first guarantees those dependencies are resolved before larger values are used.
For negative numbers, absolute value ordering preserves the correct doubling direction. For example:
-2 -> -4requires -2 to be processed before -4.
The algorithm processes every number exactly as many times as it appears. If the algorithm finishes successfully, every element has been matched into a valid doubled pair. Therefore, the array can be reordered correctly.
Complexity
Let:
n = len(arr)| Metric | Value | Why |
|---|---|---|
| Time | O(n log n) | Sorting dominates |
| Space | O(n) | Frequency map storage |
The greedy matching itself is linear.
Implementation
from collections import Counter
class Solution:
def canReorderDoubled(self, arr: list[int]) -> bool:
count = Counter(arr)
for x in sorted(arr, key=abs):
if count[x] == 0:
continue
if count[2 * x] == 0:
return False
count[x] -= 1
count[2 * x] -= 1
return TrueCode Explanation
We first count frequencies:
count = Counter(arr)Example:
arr = [4, -2, 2, -4]creates:
{
4: 1,
-2: 1,
2: 1,
-4: 1
}Then we sort by absolute value:
sorted(arr, key=abs)This gives:
[-2, 2, 4, -4]or another valid equal-absolute ordering.
For each number:
if count[x] == 0:
continueThis means the number was already used in a previous pair.
Next:
if count[2 * x] == 0:
return FalseIf no matching double exists, pairing fails immediately.
Otherwise we consume one pair:
count[x] -= 1
count[2 * x] -= 1After all numbers are processed successfully:
return TrueTesting
def run_tests():
s = Solution()
assert s.canReorderDoubled([3, 1, 3, 6]) is False
assert s.canReorderDoubled([2, 1, 2, 6]) is False
assert s.canReorderDoubled([4, -2, 2, -4]) is True
assert s.canReorderDoubled([0, 0]) is True
assert s.canReorderDoubled([1, 2, 2, 4]) is True
assert s.canReorderDoubled([-2, -4, -8, -16]) is True
assert s.canReorderDoubled([1, 2, 4, 16, 8, 4]) is False
assert s.canReorderDoubled([0, 0, 0, 0]) is True
assert s.canReorderDoubled([0, 0, 0]) is False
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
[3,1,3,6] | One value remains unmatched |
[2,1,2,6] | Missing required double |
[4,-2,2,-4] | Negative number handling |
[0,0] | Zero pairing |
[1,2,2,4] | Multiple valid chains |
| Negative doubling chain | Absolute-value ordering |
| Missing intermediate doubles | Invalid dependency chain |
| Even zero count | Valid zero matching |
| Odd zero count | Impossible zero pairing |