Contains Duplicate Solution
Given an integer array nums, return true if any value appears at least twice, and false if every element is distinct.
Problem Statement
Given an integer array nums, return true if any value appears at least twice in the array, and return false if every element is distinct.
Brute Force โ O(nยฒ)
Compare every element against every other element. Two nested loops โ the outer picks one number, the inner checks all subsequent numbers for a match. If any pair is equal, return true.
By exhaustively checking every pair (i, j) where j > i, we're guaranteed to find a duplicate if one exists. No element is missed because we cover all possible pairs.
| Metric | Value |
|---|---|
| Time | O(nยฒ) โ every pair checked |
| Space | O(1) โ no extra storage |
HashSet โ O(n)
The question boils down to: "have I seen this number before?" โ a membership test. A HashSet tracks every value we've encountered and answers that question in O(1) per lookup.
- Step 1: Create an empty HashSet<Integer>.
- Step 2: For each element
nums[i], check if it already exists in the set. - Step 3: If
set.contains(nums[i])โ returntrue(duplicate found). - Step 4: Otherwise, add
nums[i]to the set and continue. - Step 5: If we exhaust the array without a match โ return
false.
- Any time a problem asks "does a value repeat?", "are all elements unique?", or "find the first duplicate" โ think HashSet duplicate detection.
- The signal is a membership question over a growing collection.
- This same pattern appears in LC 217, LC 219 (Contains Duplicate II with index constraint), LC 128 (Longest Consecutive Sequence), and LC 349 (Intersection of Two Arrays).
- We check before inserting.
- If element X appears at indices 3 and 7, when we reach index 7 the set already holds X from index 3 โ immediate detection.
- We never need to look backward or make a second pass. The set accumulates history as we go.
Visual Walkthrough
Trace through nums = [1, 2, 3, 1]:
- We found the duplicate on the fourth element โ meaning we processed 3 elements before detecting it.
- In the worst case (no duplicates), we'd scan the entire array building up the set โ still O(n).
- In the best case (duplicate at position 1), we exit after two elements.
Code โ Java & Python
Complexity Analysis
| Approach | Time | Space | Trade-off |
|---|---|---|---|
| Brute Force | O(nยฒ) | O(1) | No extra memory, but too slow for n = 10โต |
| Sort + Linear Scan | O(n log n) | O(1)* | In-place sort mutates input; duplicates become adjacent |
| HashSet โ optimal | O(n) | O(n) | One pass, O(1) per lookup. Space for the set. |
- Sorting gives O(n log n) and lets you check adjacent elements for duplicates โ but it mutates the input array (or costs O(n) to copy first).
- The HashSet approach is faster at O(n), doesn't modify the input, and exits early when a duplicate is found.
- The trade-off is O(n) extra memory for the set. *O(1) space for sort assumes in-place; O(n) if you copy first to preserve the original.
Edge Cases & Pitfalls
| Case | Input | Why It Matters |
|---|---|---|
| Negative numbers | [-1, -2, -3, -1] | HashSet handles negatives โ hash function works the same for all integers. |
| Zeros | [0, 0] | Two zeros are duplicates. The set catches 0 on the second occurrence. |
| All duplicates | [5, 5, 5, 5] | Detected immediately at i=1 โ the set already contains 5 from i=0. |
| Single element | [1] | Minimum valid input. A single element can never have a duplicate โ false. |
| Large values | [10โน, -10โน, 10โน] | No overflow concern โ we compare values, never compute sums. HashSet handles extreme ints. |
| No duplicates | [1, 2, 3, 4, 5] | Worst case โ we scan the entire array, build a set of size n, and return false. |
set.contains() followed by set.add() as two separate calls. In Java, HashSet.add() already returns false if the element was already present โ so if (!seen.add(num)) is a single-call shortcut that checks and inserts atomically, avoiding a redundant hash computation.