Hash Maps
Use hash maps for O(1) lookups to solve problems involving pairs, frequencies, grouping, or caching computed results.
Learn & Reference
Understanding the Pattern
Hash Map Pattern
Hash maps (dictionaries in Python, objects/Maps in JavaScript) provide O(1) average-case lookup, insertion, and deletion. This makes them the go-to data structure when you need to quickly find, store, or count data.
Core Concept
A hash map stores key-value pairs. Given a key, you can:
- Insert a value in O(1)
- Lookup a value in O(1)
- Delete a value in O(1)
The hash function converts the key into an array index. Collisions are handled via chaining or open addressing.
The 4 Hash Map Archetypes
1. Complement / Pair Finding
Store seen values and check if a complement exists. This turns an O(n²) nested loop into a single O(n) pass.
2. Frequency Counting
Count occurrences of elements. Essential for "most frequent", "find duplicates", and anagram problems.
3. Grouping / Bucketing
Group elements by a shared property (anagram signature, remainder, etc.).
4. Caching / Memoization
Store computed results to avoid recalculation.
Time & Space Complexity
- Time: O(n) typical — single pass through data
- Space: O(n) for the hash map
When Hash Map Fails
- When you need sorted order (use BST or sorted array)
- When space is extremely limited (O(1) space requirement)
- When keys aren't hashable (lists in Python — convert to tuples)
- When you need range queries (use a sorted structure)
Common Mistakes
- Using the same element twice: Add to map AFTER checking, not before
- Returning values instead of indices: Read the problem carefully
- Forgetting about negative numbers: Hash maps handle negatives fine
- Using wrong key type: In Python, lists aren't hashable — use tuples
- Not handling empty input: Always consider edge cases
When to Use
Template
1# --- Complement Finding (Two Sum) ---2def two_sum(nums, target):3seen = {}4for i, num in enumerate(nums):5complement = target - num6if complement in seen:7return [seen[complement], i]8seen[num] = i9return []1011# --- Frequency Counting ---12def most_frequent(nums):13from collections import Counter14return Counter(nums).most_common(1)[0][0]1516# --- Grouping / Bucketing ---17def group_by(items, key_fn):18from collections import defaultdict19groups = defaultdict(list)20for item in items:21groups[key_fn(item)].append(item)22return dict(groups)
Syntax Reference
1# === Dictionary (built-in) ===2d = {}3d[key] = value # set4val = d[key] # get (KeyError if missing)5val = d.get(key, 0) # get with default6if key in d: ... # check existence7del d[key] # delete8for k, v in d.items(): # iterate910# === Counter (frequency counting) ===11from collections import Counter12freq = Counter([1,1,2,3]) # {1:2, 2:1, 3:1}13freq.most_common(2) # [(1,2), (2,1)]14freq[missing_key] # returns 0, not KeyError1516# === defaultdict (auto-initialize) ===17from collections import defaultdict18d = defaultdict(int) # missing keys -> 019d = defaultdict(list) # missing keys -> []20d = defaultdict(set) # missing keys -> set()
Common Recipes
1# --- Prefix Sum + Hash Map ---2# Count subarrays with sum = k3def subarray_sum(nums, k):4count, prefix = 0, 05seen = {0: 1}6for num in nums:7prefix += num8if prefix - k in seen:9count += seen[prefix - k]10seen[prefix] = seen.get(prefix, 0) + 111return count1213# --- First Unique ---14def first_unique(s):15freq = Counter(s)16for i, ch in enumerate(s):17if freq[ch] == 1:18return i19return -12021# --- Group Anagrams ---22def group_anagrams(strs):23groups = defaultdict(list)24for s in strs:25key = tuple(sorted(s))26groups[key].append(s)27return list(groups.values())
Complexity
| Operation | Average | Worst Case |
|---|---|---|
| Insert | O(1) | O(n) |
| Lookup | O(1) | O(n) |
| Delete | O(1) | O(n) |
| Iterate | O(n) | O(n) |
Watch Out
- ✗When you need sorted order (use BST or sorted array)
- ✗When space is extremely limited (O(1) space requirement)
- ✗When keys aren't hashable (lists in Python — convert to tuples)
- ✗When you need range queries (use a sorted structure)
- ✗Using the same element twice: Add to map AFTER checking, not before
- ✗Returning values instead of indices: Read the problem carefully
- ✗Forgetting about negative numbers: Hash maps handle negatives fine
- ✗Using wrong key type: In Python, lists aren't hashable — use tuples
- ✗Not handling empty input: Always consider edge cases