Overview
A hash set stores unique values and answers 'have I seen this?' in O(1) on average. It is a hash map with keys only. Adding a value that already exists has no effect.
Sets are the simplest way to remove duplicates, check membership, and compute intersections and unions. Many O(n^2) 'does it exist' loops become O(n) by putting one side into a set.
The bouncer only needs to know whether a name is on the list, not how many times it appears or in what order it was added. Checking a name takes the same time whether the list has 10 or 10,000 names.
When to use it
- Detecting duplicates or cycles (visited states).
- Fast membership tests: is this word in the dictionary?
- Removing duplicates while preserving first occurrence.
- Set algebra: intersection, union, difference.
- Visited tracking in BFS and DFS.
Problem patterns it solves
Recognize it when: contains duplicate, first repeated, find the duplicate.
- 217. Contains Duplicate
- 219. Contains Duplicate II
- 1876. Substrings of Size Three with Distinct Characters
Recognize it when: does x - 1 or target - x exist; is the word in a dictionary.
- 128. Longest Consecutive Sequence
- 771. Jewels and Stones
- 139. Word Break
Recognize it when: common elements, elements in one but not the other.
- 349. Intersection of Two Arrays
- 2215. Find the Difference of Two Arrays
- 1002. Find Common Characters
Recognize it when: a process repeats; detect a repeated state.
- 202. Happy Number
- 36. Valid Sudoku
- 957. Prison Cells After N Days
Recognize it when: BFS / DFS where nodes or states can repeat.
- 127. Word Ladder
- 752. Open the Lock
- 841. Keys and Rooms
Where it is used in real software
Counting unique users uses a set for exact counts; at massive scale, HyperLogLog estimates the size of a set with a few kilobytes.
Firewalls and spam filters check IPs and domains against sets; Bloom filters provide a memory-efficient probabilistic version.
Web crawlers keep a visited-URL set so each page is fetched once.
A user's roles or scopes are a set; checking access is a membership test.
Key terms
- add
- Insert a value; no effect if already present. O(1) average.
- has / contains
- Membership test. O(1) average.
- delete / remove
- Remove a value. O(1) average.
- Bloom filter
- Probabilistic set: no false negatives, small chance of false positives, very little memory.
Using a set effectively
- 1Decide what identity means
Numbers and strings compare by value. For pairs or coordinates, encode a string key like `${r},${c}` in JS, or use a record in Java.
- 2Check before adding when you need to detect repeats
if (set.has(x)) duplicate found; else set.add(x).
- 3Build from an array for membership tests
new Set(arr) is O(n); every later has() is O(1).
- 4Remove values to maintain a window
In sliding window problems, delete the value leaving the window.
Happy number cycle detection
n = 2: replace n with the sum of the squares of its digits
| n | Next value | Seen before? | Set size after |
|---|---|---|---|
| 2 | 4 | no | 1 |
| 4 | 16 | no | 2 |
| 16 | 37 | no | 3 |
| 37 | 58 | no | 4 |
| 58 | 89 | no | 5 |
| 89 | 145 | no | 6 |
| 145 | 42 | no | 7 |
| 42 | 20 | no | 8 |
| 20 | 4 | no | 9 |
| 4 | - | yes: cycle | stop, not happy |
NOWn: 2 | Next value: 4 | Seen before?: no | Set size after: 1
4 repeats, so the process loops forever without reaching 1. The set detects the cycle in O(number of states); Floyd's fast and slow pointers can do it with O(1) space.
Implementation
const containsDuplicate = (nums) => new Set(nums).size !== nums.length; // 219: duplicate within distance k (set as a sliding window)function containsNearbyDuplicate(nums, k) { const window = new Set(); for (let i = 0; i < nums.length; i++) { if (window.has(nums[i])) return true; window.add(nums[i]); if (window.size > k) window.delete(nums[i - k]); } return false;} function isHappy(n) { const seen = new Set(); while (n !== 1 && !seen.has(n)) { seen.add(n); let next = 0; while (n > 0) { const d = n % 10; next += d * d; n = Math.floor(n / 10); } n = next; } return n === 1;} // Set algebraconst a = new Set([1, 2, 3]), b = new Set([2, 3, 4]);const intersection = [...a].filter((x) => b.has(x)); // [2, 3]const union = new Set([...a, ...b]); // {1, 2, 3, 4}const difference = [...a].filter((x) => !b.has(x)); // [1] // Coordinates: encode as stringsconst visited = new Set();visited.add(`${2},${3}`);Complexity and performance
Same as hash map.
One add per element.
Build one set, probe with the other.
Sorted, supports floor and ceiling.
Trade-offs
A set gives O(n) time and O(n) space. Sorting gives O(n log n) time and O(1) extra space (if in place).
A Bloom filter uses a tiny fraction of the memory of a set but can report false positives.
For values in a small known range, a boolean array or bitset is faster.
Variants and related techniques
Ordered iteration, floor, ceiling, and range views in O(log n).
One bit per possible value; ideal for dense small ranges.
For values 1..n in an array of size n, mark presence by negating arr[value - 1] for O(1) extra space.
Common mistakes
- Adding arrays or objects to a JS Set and expecting value equality.
Fix: Sets compare objects by reference. Encode them as strings or numbers first.
- Using a set when counts matter.
Fix: Use a map of counts when duplicates must be counted.
- Iterating a set while deleting in Java.
Fix: Use an Iterator's remove(), or collect items to remove first.
Interview questions
Hash set vs hash map?
A set stores only keys and answers membership. A map associates each key with a value, such as a count or an index.
How would you find duplicates using O(1) extra space?
Sort in place and compare neighbors (O(n log n)), or, when values are in 1..n, mark indexes by negation, or use Floyd's cycle detection as in Find the Duplicate Number.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| 217. Contains Duplicate | Easy | Basic membership. |
| 202. Happy Number | Easy | Detect repeated states. |
| 349. Intersection of Two Arrays | Easy | Set algebra. |
| 219. Contains Duplicate II | Easy | Set as a sliding window. |
| 36. Valid Sudoku | Medium | Encoded keys. |
| 128. Longest Consecutive Sequence | Medium | O(n) with membership. |