Find All Duplicates in Array
Given an array of integers nums, each element in the array either appears once or twice. Return a list containing all the numbers that appear twice.
Note: Each element in nums is between one and nums.length (inclusive).
Example(s)
Example 1:
Input: nums = [2, 3, 1, 5, 5]
Output: [5]
Explanation:
- 2 appears once
- 3 appears once
- 1 appears once
- 5 appears twice
Only 5 appears twice.
Example 2:
Input: nums = [1, 2, 3]
Output: []
Explanation:
All numbers appear exactly once, so no duplicates.
Example 3:
Input: nums = [7, 2, 2, 3, 3, 4, 4]
Output: [2, 3, 4]
Explanation:
- 2 appears twice
- 3 appears twice
- 4 appears twice
- 7 appears once
Solution
The solution uses the array as a hash map:
Since values are between 1 and n (where n = array length), we can use indices as keys:
- Use array indices: For each value, use it as an index
- Mark visited: Negate the value at that index to mark it as seen
- Detect duplicates: If we see a negative value, we've seen this number before
- Collect duplicates: Add to result when we find a duplicate
- JavaScript Solution
- Python Solution
JavaScript Solution - Array as Hash Map
/**
* Find all numbers that appear twice
* @param {number[]} nums - Array of integers (1 to n)
* @return {number[]} - Array of numbers that appear twice
*/
function findDuplicates(nums) {
const result = [];
for (let i = 0; i < nums.length; i++) {
// Use absolute value since we might have negated it
const index = Math.abs(nums[i]) - 1;
// If value at index is negative, we've seen this number before
if (nums[index] < 0) {
result.push(Math.abs(nums[i]));
} else {
// Mark as seen by negating
nums[index] = -nums[index];
}
}
return result;
}
// Test cases
console.log('Example 1:', findDuplicates([2, 3, 1, 5, 5])); // [5]
console.log('Example 2:', findDuplicates([1, 2, 3])); // []
console.log('Example 3:', findDuplicates([7, 2, 2, 3, 3, 4, 4])); // [2, 3, 4]
console.log('Test 4:', findDuplicates([4, 3, 2, 7, 8, 2, 3, 1])); // [2, 3]Output:
Click "Run Code" to execute the code and see the results.
Python Solution - Array as Hash Map
from typing import List
def find_duplicates(nums: List[int]) -> List[int]:
"""
Find all numbers that appear twice
Args:
nums: List of integers (1 to n)
Returns:
List[int]: List of numbers that appear twice
"""
result = []
for i in range(len(nums)):
# Use absolute value since we might have negated it
index = abs(nums[i]) - 1
# If value at index is negative, we've seen this number before
if nums[index] < 0:
result.append(abs(nums[i]))
else:
# Mark as seen by negating
nums[index] = -nums[index]
return result
# Test cases
print('Example 1:', find_duplicates([2, 3, 1, 5, 5])) # [5]
print('Example 2:', find_duplicates([1, 2, 3])) # []
print('Example 3:', find_duplicates([7, 2, 2, 3, 3, 4, 4])) # [2, 3, 4]
print('Test 4:', find_duplicates([4, 3, 2, 7, 8, 2, 3, 1])) # [2, 3]Loading Python runtime...
Output:
Click "Run Code" to execute the code and see the results.
Alternative Solution (Hash Set)
Here's a simpler approach using a hash set:
- JavaScript Hash Set
- Python Hash Set
/**
* Hash set approach (simpler but uses O(n) space)
*/
function findDuplicatesHashSet(nums) {
const seen = new Set();
const result = [];
for (const num of nums) {
if (seen.has(num)) {
result.push(num);
} else {
seen.add(num);
}
}
return result;
}
def find_duplicates_hash_set(nums: List[int]) -> List[int]:
"""
Hash set approach (simpler but uses O(n) space)
"""
seen = set()
result = []
for num in nums:
if num in seen:
result.append(num)
else:
seen.add(num)
return result
Alternative: Frequency Counter
Here's an approach using frequency counting:
- JavaScript Frequency
- Python Frequency
/**
* Frequency counter approach
*/
function findDuplicatesFrequency(nums) {
const frequency = new Map();
// Count frequencies
for (const num of nums) {
frequency.set(num, (frequency.get(num) || 0) + 1);
}
// Find all numbers that appear twice
const result = [];
for (const [num, count] of frequency.entries()) {
if (count === 2) {
result.push(num);
}
}
return result;
}
from collections import Counter
def find_duplicates_frequency(nums: List[int]) -> List[int]:
"""
Frequency counter approach
"""
frequency = Counter(nums)
# Find all numbers that appear twice
return [num for num, count in frequency.items() if count == 2]
Complexity
- Time Complexity:
- Array as hash map: O(n) - Single pass through the array
- Hash set: O(n) - Single pass through the array
- Frequency counter: O(n) - Count frequencies, then filter
- Space Complexity:
- Array as hash map: O(1) - Only uses result array (not counted in space complexity)
- Hash set: O(n) - For the hash set
- Frequency counter: O(n) - For the frequency map
Approach
The optimal solution uses the array as a hash map:
- Key insight: Since values are between 1 and n, we can use indices 0 to n-1
- Marking technique:
- For value
x, check indexx - 1 - If
nums[x-1]is negative, we've seenxbefore → it's a duplicate - Otherwise, negate
nums[x-1]to markxas seen
- For value
- Absolute value: Use
Math.abs()since we negate values - Collect duplicates: Add to result when we detect a duplicate
Key Insights
- Array as hash map: Leverages the constraint that values are 1 to n
- Negation trick: Use negative sign to mark visited without extra space
- O(1) space: Optimal space complexity (excluding result array)
- Single pass: Process array once
- Index mapping: Value
xmaps to indexx - 1
Step-by-Step Example
Let's trace through Example 1: nums = [2, 3, 1, 5, 5]
Initial: nums = [2, 3, 1, 5, 5]
result = []
i = 0: nums[0] = 2
index = abs(2) - 1 = 1
nums[1] = 3 (positive, not seen)
nums[1] = -3 (mark as seen)
nums = [2, -3, 1, 5, 5]
i = 1: nums[1] = -3
index = abs(-3) - 1 = 2
nums[2] = 1 (positive, not seen)
nums[2] = -1 (mark as seen)
nums = [2, -3, -1, 5, 5]
i = 2: nums[2] = -1
index = abs(-1) - 1 = 0
nums[0] = 2 (positive, not seen)
nums[0] = -2 (mark as seen)
nums = [-2, -3, -1, 5, 5]
i = 3: nums[3] = 5
index = abs(5) - 1 = 4
nums[4] = 5 (positive, not seen)
nums[4] = -5 (mark as seen)
nums = [-2, -3, -1, 5, -5]
i = 4: nums[4] = -5
index = abs(-5) - 1 = 4
nums[4] = -5 (negative, already seen!)
result.push(5)
result = [5]
Final: result = [5]
Visual Representation
Example 1: nums = [2, 3, 1, 5, 5]
Value → Index mapping:
2 → index 1
3 → index 2
1 → index 0
5 → index 4
Process:
See 2 → mark index 1
See 3 → mark index 2
See 1 → mark index 0
See 5 → mark index 4
See 5 → index 4 already marked → duplicate!
Result: [5]
Edge Cases
- No duplicates: Return empty array
- All duplicates: If all appear twice, return all values
- Single element: Return empty array (can't have duplicate of one)
- All same value: If all are the same and appear twice, return that value
- Large array: Works efficiently with O(n) time
Important Notes
- Values 1 to n: Constraint allows using indices as hash keys
- In-place marking: Negation doesn't require extra space
- Absolute value: Must use abs() since we negate values
- Modifies input: The optimal solution modifies the input array
- Result order: Order of duplicates in result may vary
Why Array as Hash Map Works
Since values are between 1 and n:
- Value
xcan be used as indexx - 1 - We have exactly n indices (0 to n-1)
- Perfect hash function: no collisions
- Can mark visited by negating the value at that index
Related Problems
- Find the Duplicate Number: Find one duplicate (different constraint)
- Contains Duplicate: Check if duplicates exist
- First Missing Positive: Similar array manipulation
- Set Mismatch: Find duplicate and missing number
Takeaways
- Array as hash map is powerful when values are 1 to n
- Negation trick marks visited without extra space
- O(1) space is achievable with careful manipulation
- Single pass is optimal
- Constraint utilization is key to optimization