Hello! Welcome to your next lesson on hashing.
In our last lesson, we mastered the "Two Sum" problem using hash maps to store key-value pairs (value -> index) for instant lookups. This demonstrated a powerful time-space tradeoff and the core idea of searching for a "complement."
Today, we'll focus on a simpler but equally powerful hash-based structure: the hash set. Your learning outcome is to use hash sets for efficient duplicate detection and membership testing. While a hash map stores pairs, a hash set stores only unique items, making it the perfect tool when all you need to know is whether you've seen an item before. This is a fundamental pattern for many interview problems.
1. What is a Hash Set?
A hash set is a data structure that stores a collection of unique items. Think of it as a hash map where you only care about the keys, not the values. Its primary purpose is to answer one question extremely quickly: "Is this item in the collection?"
The three main operations on a hash set are:
- Add: Adds an item to the set. If the item is already there, nothing changes.
- Contains: Checks if an item exists in the set.
- Remove: Deletes an item from the set.
Because hash sets are built on top of hash tables, these operations have an average time complexity of O(1).
Hash Tables: Hash Functions, Sets & Maps
To start, let's get a formal definition. The article 'Hash Tables: Hash Functions, Sets & Maps' provides a concise explanation of what a hash set is, its implementation in Python, and its performance characteristics.
Please read the sections titled 'Hash Sets: Unique Data Storage', 'Python’s Built-in Hash Structures', and 'Performance and Use Cases'. Focus on how a hash set guarantees uniqueness and provides O(1) average time complexity for its core operations.
How do Hash Sets achieve O(1) speed?
The "magic" of O(1) average time complexity comes from the underlying hash table. To build your intuition, it's helpful to see how one works under the hood. When you add an item, the set:
- Computes a hash code for the item (an integer).
- Uses this hash code to calculate an index into an underlying array.
- Stores the item at that index.
When you check if an item is in the set, it repeats steps 1 and 2 to instantly find the correct index and see if the item is there, without needing to scan the entire collection.
Python Hash Sets Explained & Demonstrated - Computerphile
This video from Computerphile gives an excellent visual demonstration of how a hash set is built from scratch. It will clarify concepts like hashing, collisions, and why the complexity is O(1) on average but can be O(n) in the worst case.
Watch from the beginning to 08:44. Pay attention to: The goal of O(1) complexity. How a hash function converts an item into an index. How collisions (two items hashing to the same index) are handled with chaining. The difference between the fast average case and the rare, slow worst case.
A key requirement for an object to be stored in a hash set is that it must be hashable, meaning its value cannot change. This is why you can add an integer, string, or tuple to a Python set, but you cannot add a list or dictionary, as they are mutable.
2. Application: The "Contains Duplicate" Problem
Now, let's apply this knowledge to a classic interview question that perfectly illustrates the power of hash sets: Contains Duplicate (LeetCode 217).
Problem Statement: Given an integer array nums, return true if any value appears at least twice in the array, and false if every element is distinct.
Example 1:
- Input:
nums = [1, 2, 3, 1] - Output:
true(because1appears twice)
Example 2:
- Input:
nums = [1, 2, 3, 4] - Output:
false
From Brute-Force to Optimal
As always, let's consider the straightforward approaches first to understand the tradeoffs.
Contains Duplicate - Leetcode 217 - Python
The video 'Contains Duplicate' from NeetCode provides a complete walkthrough of the problem, starting with the less efficient solutions and building up to the optimal hash set approach.
Watch from the beginning to 03:17. This part covers the brute-force (O(n²)) and sorting (O(n log n)) approaches. Understanding these is crucial for justifying why the hash set method is superior in an interview.
The brute-force and sorting solutions are valid, but we can achieve a linear time solution by using a hash set to keep track of the numbers we've seen.
The algorithm is simple:
- Initialize an empty hash set, let's call it
seen. - Iterate through each number
nin the input arraynums. - For each
n, check ifnis already inseen.- If it is, we've found a duplicate. Return
true. - If it's not, add
ntoseento mark that we've now encountered it.
- If it is, we've found a duplicate. Return
- If we finish the loop without finding any duplicates, it means all elements were unique. Return
false.
Contains Duplicate - Leetcode 217 - Python
Now, let's watch the optimal solution.
Continue watching from 03:17 to the end (06:17). Focus on how the hash set is used to track 'seen' elements, enabling an O(1) check for duplicates at each step. Note the clean Python implementation.
Here's the Python code for this approach:
def contains_duplicate(nums: list[int]) -> bool:
seen = set() # Initialize an empty hash set
for n in nums:
if n in seen:
return True
seen.add(n)
return False
- Time Complexity: . We iterate through the array once. Each
addandinoperation on the set is on average. - Space Complexity: . In the worst case (no duplicates), the set will grow to store all
nelements from the input array.
This is another classic time-space tradeoff. We use extra memory to achieve a faster, linear-time algorithm.
Test your understanding!
Consider the input nums = [1, 5, 2, 3, 5, 6]. Trace the hash set algorithm. What is the state of the seen set just before the function returns true?
Show answer
n = 1.1is not inseen. Add it.seen = {1}.n = 5.5is not inseen. Add it.seen = {1, 5}.n = 2.2is not inseen. Add it.seen = {1, 5, 2}.n = 3.3is not inseen. Add it.seen = {1, 5, 2, 3}.n = 5.5is inseen. A duplicate is found. The function returnstrue.
The state of the seen set just before returning is {1, 5, 2, 3}.
The "Pythonic" Shortcut
Python's set type is so convenient that it allows for an elegant one-liner to solve this problem. Since a set can only contain unique elements, if you convert a list to a set, any duplicates are automatically removed.
217. Contains Duplicate - Solution & Explanation
The NeetCode website provides a text summary of this logic and also introduces a very clever and concise one-liner solution in Python that you should know.
Read the sections '3. Hash Set' and '4. Hash Set Length'. The second one explains the logic behind comparing the length of the original list to the length of a set created from it.
The logic is: if the length of the set is less than the length of the original list, it must be because duplicates were removed.
def contains_duplicate_oneliner(nums: list[int]) -> bool:
return len(set(nums)) < len(nums)
This single line of code also has time complexity (to build the set) and space complexity (to store the set).
3. Performance in Context
The reason we choose a hash set for duplicate detection and membership testing is its unmatched average-case performance. Other data structures could do the job, but less efficiently. For example, a sorted data structure (like a balanced binary search tree, which backs Java's TreeSet) could also check for membership, but it would take time.

When an interview problem asks you to check for duplicates or manage a collection of unique items without needing them to be ordered, your first thought should be a hash set.
Conclusion
You have now added another essential hash-based pattern to your toolkit. While hash maps are for associating keys with values, hash sets are for managing collections of unique items.
Key Takeaways:
- Hash Sets Store Unique Items: They are designed to enforce uniqueness and provide extremely fast operations.
- O(1) Operations: Adding, removing, and checking for an item in a hash set are all on average.
- Ideal for Duplicate Detection: The "Contains Duplicate" problem is the canonical example of using a hash set to track seen elements.
- Time-Space Tradeoff: The optimal time solution requires space, which is a common and acceptable tradeoff in interviews.
In our next lesson, we will continue exploring problems that can be solved with hashing. We'll focus on anagram and character-based problems, where you'll use hash maps to efficiently count character frequencies, combining ideas from our last two lessons.