Skip to content

Coding patternsBlind 75

Contains Duplicate

A set remembers every number seen so far, so a repeat shows up in one pass. Solved three ways, brute force in O(n²), sorting in O(n log n) and a hash set in O(n), with a step-through, code in five languages and the Big O worked out from the code.

You are given an array nums of whole numbers. Call its length n. Return true if some value appears in it at least twice. Return false if every value appears only once.

Example 1

Input: nums = [3, 8, 1, 6, 2, 8]

Output: true

The value 8 appears twice, at index 1 and at index 5.

nums
308112632485

Example 2

Input: nums = [5, 2, 9, 4]

Output: false

All four values are different.

Example 3

Input: nums = [7]

Output: false

A single number has nothing to repeat with.

  • 1 ≤ n ≤ 100,000
  • −1,000,000,000 ≤ nums[i] ≤ 1,000,000,000
Do we need to say which value repeats?
No. Only true or false. So we can stop at the first repeat we find.
Can the numbers be negative?
Yes. That rules out using the value as an index into a plain array. A hash set handles any value.
Is the array sorted?
No. If it were, equal values would already sit next to each other.
May we change the array?
Usually yes. That matters for the sorting approach, which sorts the array in place.

The question is “have I seen this value before?”, asked once for every number. Scanning back over the earlier numbers to answer it is slow. A hash set remembers what we have seen and answers that question in one step. That is the hashing pattern: spend memory so you never search twice.

Hash set O(n)

Walk the array once. Keep a hash set of the numbers seen so far. A hash set is a collection that can tell you “is this value inside?” in one step on average, however many values it holds.

For each number, ask the set first. If the number is already there, we have found a repeat. If not, add it, so a later copy can find it.

Why it never misses a repeat: when the second copy of any value arrives, the first copy is already in the set, so the check on that step must say yes.

nums
308112632485
seen
empty
  1. seen = set()
  2. for number in nums:
  3. if number in seen:
  4. return True
  5. seen.add(number)
  6. return False

Start with an empty set. A set answers "have I seen this number?" in one step.

1 / 8
Solution.javasolution.pySolution.cssolution.gosolution.ts
class Solution {
// Hash set: remember every number seen so far.
// One pass with O(1) lookups, so O(n) time and O(n) memory.
public boolean containsDuplicate(int[] nums) {
Set<Integer> seen = new HashSet<>();
for (int number : nums) {
// A set lookup is one step on average, so this check is cheap.
if (seen.contains(number)) return true;
seen.add(number);
}
return false;
}
}
Time O(n)Space O(n)Here is how we get these from the code, in four steps.
  1. 1 Find the work that repeats

    Each run of the loop does one set lookup and at most one set add. Both take one step on average, so each run counts as 1 step. There is one loop, and nothing inside it loops again.

    1. seen = set()
    2. for number in nums:
    3. if number in seen:
    4. return True
    5. seen.add(number)
    6. return False
  2. 2 Count the runs on Example 1

    Example 1 has 6 numbers. The loop runs once per number, and the 6th run finds the second 8.

    6 runs. With no repeat, the loop would also run 6 times and then return false.

    run123456
    number381628
    in seen?nononononoyes
    thenaddaddaddaddaddreturn
  3. 3 Write the count for any n

    The loop visits each number at most once, so in the worst case it runs:

    n times

    Check it with Example 1: 6 numbers, 6 runs.

  4. 4 Keep only the part that grows fastest

    There is nothing to drop. The count is already n.

    So the time is O(n).

  5. S Space: count the extra memory the same way

    The set is the extra memory. With no repeat, every number goes into it, so it ends up holding n values. The space is O(n). This is the trade: the set costs memory and saves the second loop.

    seen (up to n numbers)number

2,000 random arrays of 1 to 8 numbers from a tiny range, so a repeat shows up about half the time. The brute force, the sort first and the hash set gave the same answer every time.

Largest inputsResult
100,000 numbers (the most allowed), all different, so every number is read and stored. Hash set.0.01 s, and about 6 MB of extra memory: the set holds all 100,000 numbers, which is the O(n) space.
100,000 numbers, all different. Sort first.0.02 s, and about 0.4 MB of extra memory: it sorts in place, and the sort borrows a little room of its own.
2,000 numbers. Brute force, about 2 million pairs.0.05 s. At 100,000 numbers it would check 5 billion pairs, which is why the hash set matters.