442. Find All Duplicates in an Array - Explanation
Description
The array nums has length n. Each value is between 1 and n, and no value occurs more than twice. Return every value that occurs twice, listing each repeated value once. You may return the values in any order. Your algorithm should run in O(n) time and use O(1) auxiliary space, excluding the returned array. You may modify nums.
Example 1:
Input: nums = [3,1,3,2,2]
Output: [2,3]Explanation: Values 2 and 3 each appear twice, while 1 appears once. Return [2,3]; [3,2] is also valid.
Example 2:
Input: nums = [2,1,3]
Output: []Explanation: Every value appears exactly once, so there are no duplicates and the result is empty.
Example 3:
Input: nums = [1,1]
Output: [1]Explanation: The value 1 appears twice. Include it once in the returned array.
Constraints:
1 <= n <= 1000001 <= nums[i] <= n- Every value occurs at most twice.
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- Hash Sets and Hash Maps - Used for O(1) lookup to track seen elements or count frequencies
- Array Index Manipulation - Understanding how values in range [1, n] can map to indices [0, n-1]
- In-Place Array Modification - The optimal solution uses negative marking to avoid extra space
1. Brute Force
Intuition
The most direct approach is to compare every element with every other element that comes after it. If we find two elements that are equal, we know that value appears twice (since the problem states each element appears at most twice).
Algorithm
- For each element at index
i:- Compare it with every element at index
jwherej > i. - If
nums[i] == nums[j], addnums[i]to the result and break (no need to check further for this value).
- Compare it with every element at index
- Return the result list.
Time & Space Complexity
- Time complexity:
- Space complexity: extra space.
2. Sorting
Intuition
After sorting, duplicate values will be adjacent to each other. We can simply scan through the sorted array and check if any element equals its neighbor. This is more efficient than the brute force approach since we only need one pass after sorting.
Algorithm
- Sort the array.
- Iterate through the array from index
0ton-2:- If
nums[i] == nums[i+1], addnums[i]to the result.
- If
- Return the result list.
Time & Space Complexity
- Time complexity:
- Space complexity: or depending on the sorting algorithm.
3. Hash Set
Intuition
We can use a hash set to track which numbers we have already seen. As we iterate through the array, if a number is already in the set, it must be a duplicate. Otherwise, we add it to the set. This gives us O(1) lookup time for each element.
Algorithm
- Initialize an empty hash set
seenand an empty result list. - For each number in the array:
- If it is already in
seen, add it to the result. - Otherwise, add it to
seen.
- If it is already in
- Return the result list.
Time & Space Complexity
- Time complexity:
- Space complexity:
4. Hash Map
Intuition
Instead of just tracking presence, we can count how many times each number appears using a hash map. After counting, we iterate through the map and collect all numbers that appear exactly 2.
Algorithm
- Build a frequency map counting occurrences of each number.
- Iterate through the map:
- If a number has a count of
2, add it to the result.
- If a number has a count of
- Return the result list.
Time & Space Complexity
- Time complexity:
- Space complexity:
5. Negative Marking
Intuition
Since all values are in the range [1, n] where n is the array length, we can use the array itself as a hash map. The key insight is that each value v can be mapped to index v-1. When we encounter a value, we mark the element at its corresponding index as negative. If we encounter a value whose corresponding index is already negative, that value must be a duplicate.
Algorithm
- For each number in the array:
- Compute the index as
abs(num) - 1. - If
nums[idx]is already negative, addabs(num)to the result (this value was seen before). - Negate
nums[idx]to mark that we have seen the valueidx + 1.
- Compute the index as
- Return the result list.
Time & Space Complexity
- Time complexity:
- Space complexity: extra space.
Common Pitfalls
Forgetting to Use Absolute Value When Accessing Indices
In the negative marking approach, once a value at an index is negated, subsequent accesses using that value as an index will fail. Always use abs(num) - 1 to compute the correct index, as the value itself may have been negated in a previous iteration.
Off-by-One Index Errors
Since array values are in the range [1, n] but indices are [0, n-1], you must subtract 1 when converting a value to an index. Forgetting this adjustment causes index out of bounds errors or marks the wrong positions.
Modifying the Array When It Should Be Preserved
The negative marking approach modifies the input array in place. If the problem requires the original array to remain unchanged, or if the array is used elsewhere after the function call, you should either restore the array afterward or use a different approach like a hash set.
Sign in to join the discussion