Hash Maps and Sets: The Most Underrated FAANG Topic

Ask candidates what they’re grinding before a FAANG loop and you’ll hear dynamic programming, graphs, maybe trees. Almost nobody says “hash maps.” Yet look at any list of most-frequently-asked interview questions and hash maps are everywhere — Two Sum, Group Anagrams, Longest Consecutive Sequence, Subarray Sum Equals K, Top K Frequent Elements.

Here’s the uncomfortable math: you are far more likely to face a hash map problem than a dynamic programming problem, and hash map problems are faster to get good at. That makes them the highest-ROI topic in interview prep — and the most underrated.

This post covers why, then walks through the three patterns that generate nearly every hash map question, with five fully-solved problems in Python.


Why Hash Maps Are Underrated

Three reasons candidates skip them, and why each one is wrong:

  1. “They’re too basic.” Knowing what a dictionary is differs from recognizing, mid-interview, that a hash map turns your O(n²) idea into O(n). The data structure is basic; the pattern recognition isn’t.
  2. They hide inside other problems. String questions, array questions, even graph questions frequently have a hash map at the core of the optimal solution. Interviewers love these because the naive solution works — it’s just slow — which cleanly separates candidates who can optimize from those who can’t.
  3. They’re the follow-up goldmine. “What’s the worst-case lookup time?” “How does Python handle collisions?” Candidates who breezed past hash maps as “easy” get caught flat here.

The core superpower is simple: a hash map trades O(n) memory for O(1) average-case lookup. Nearly every hash map interview problem is some version of “replace a repeated linear scan with a constant-time lookup.” Brute force scans; the optimal solution remembers. (If you want the internals — hash functions, buckets, collision resolution — Day 23: Hash Tables and our hashing deep dive cover them.)

Let’s see the three patterns.


Pattern 1: Frequency Counting

The idea: count occurrences in one pass, then answer questions from the counts. Any problem mentioning duplicates, counts, “most common,” or anagrams starts here.

Problem 1: Valid Anagram

Given two strings, return True if one is an anagram of the other.

Two strings are anagrams when their character counts match — so count both and compare:

 1def is_anagram(s, t):
 2    if len(s) != len(t):
 3        return False
 4
 5    counts = {}
 6    for ch in s:
 7        counts[ch] = counts.get(ch, 0) + 1
 8    for ch in t:
 9        if counts.get(ch, 0) == 0:
10            return False
11        counts[ch] -= 1
12    return True

Complexity: O(n) time, O(k) space where k is the alphabet size. Compare that with the “obvious” alternative — sorting both strings — which costs O(n log n). Mentioning that trade-off unprompted is exactly the kind of signal interviewers listen for.

In Python you’d reach for collections.Counter, and you should say so:

1from collections import Counter
2
3def is_anagram(s, t):
4    return Counter(s) == Counter(t)

Interviewers generally allow Counter, but be ready to implement the manual version — asking “can you do that without Counter?” is a classic move.

Problem 2: Top K Frequent Elements

Given an integer array and k, return the k most frequent elements.

 1from collections import Counter
 2
 3def top_k_frequent(nums, k):
 4    counts = Counter(nums)
 5
 6    # Bucket index = frequency. An element appears at most len(nums) times.
 7    buckets = [[] for _ in range(len(nums) + 1)]
 8    for num, freq in counts.items():
 9        buckets[freq].append(num)
10
11    result = []
12    for freq in range(len(buckets) - 1, 0, -1):  # highest frequency first
13        for num in buckets[freq]:
14            result.append(num)
15            if len(result) == k:
16                return result

Frequency counting is step one; the interesting part is extracting the top k. A heap gives O(n log k); the bucket trick above — indexing an array by frequency — gets O(n). Presenting the heap version first and the bucket version as an optimization is a strong interview arc.


Pattern 2: The Two Sum Family (Complement Lookup)

The idea: as you scan, ask “have I already seen the thing that completes this element?” — and let a hash map answer in O(1). This single idea powers an entire family of “find a pair/subarray that satisfies X” problems.

Problem 3: Two Sum

Given an array and a target, return the indices of two numbers that add up to the target.

The brute force checks all pairs — O(n²). Instead, store each number’s index as you go, and for each new number ask whether its complement (target - num) has already appeared:

1def two_sum(nums, target):
2    seen = {}  # value -> index
3    for i, num in enumerate(nums):
4        complement = target - num
5        if complement in seen:
6            return [seen[complement], i]
7        seen[num] = i

Complexity: O(n) time, O(n) space. One pass, one lookup per element. This is the most-asked interview question in existence, and the reason is pedagogical: it’s the purest demonstration of trading space for time.

Problem 4: Subarray Sum Equals K

Given an integer array and k, return the number of contiguous subarrays whose sum equals k.

This looks like a sliding-window problem, but negative numbers break the window logic. The trick is prefix sums plus the complement idea: the sum of subarray (i, j] equals prefix[j] - prefix[i], so a subarray summing to k ends at position j exactly when some earlier prefix equals prefix[j] - k. Count prefix sums in a hash map as you scan:

 1def subarray_sum(nums, k):
 2    count = 0
 3    prefix = 0
 4    seen = {0: 1}  # prefix sum -> how many times it has occurred
 5
 6    for num in nums:
 7        prefix += num
 8        count += seen.get(prefix - k, 0)   # complements seen so far
 9        seen[prefix] = seen.get(prefix, 0) + 1
10    return count

Complexity: O(n) time, O(n) space — versus O(n²) for checking every subarray. The {0: 1} initialization handles subarrays that start at index 0, and forgetting it is the classic bug. Same skeleton as Two Sum: one pass, complement lookup, record what you’ve seen. Once you see this problem as Two Sum, a half-dozen “count the subarrays” questions fall to the identical technique.


Pattern 3: Grouping by Canonical Key

The idea: items belong to the same group if they share some property. Compute a canonical key representing that property, and use it as a hash map key — the map does the grouping for free.

Problem 5: Group Anagrams

Given an array of strings, group the anagrams together.

All anagrams share the same sorted spelling, so the sorted string is a perfect canonical key:

1from collections import defaultdict
2
3def group_anagrams(strs):
4    groups = defaultdict(list)
5    for s in strs:
6        key = "".join(sorted(s))   # canonical form: anagrams collide on purpose
7        groups[key].append(s)
8    return list(groups.values())

Complexity: O(n · m log m) for n strings of length m, dominated by sorting each key. The optimization follow-up: use a character-count tuple (tuple of 26 counts) as the key instead, dropping to O(n · m) — pattern 1 feeding pattern 3.

The entire skill in grouping problems is designing the key. Sorted string for anagrams, (row - col) for chessboard diagonals (the same trick that powers the N-Queens pruning in our backtracking guide), normalized slope for points on a line. Once the key is right, the code writes itself.


And Don’t Forget Sets

A set is a hash map when you only care about membership — same O(1) lookup, less ceremony. The one-liner that shows up over and over: “have I seen this before?”

1def has_duplicate(nums):
2    seen = set()
3    for num in nums:
4        if num in seen:
5            return True
6        seen.add(num)
7    return False

Sets also star in Longest Consecutive Sequence, cycle detection (Happy Number), and the visited-set in every graph traversal you’ll ever write. Day 24: Sets covers the operations, and our dictionaries-and-sets walkthrough shows more practical uses.


Pattern Recognition Cheat Sheet

The problem says…Reach for…
“duplicates”, “count”, “most frequent”, “anagram of”Frequency counting (Counter)
“pair that sums to”, “subarray with sum”Complement lookup / prefix sums
“group the…”, “belong together”Canonical key grouping
“have we seen…”, “contains”, “visited”Set membership

And the follow-up answers to have loaded: hash map operations are O(1) average but O(n) worst case when many keys collide in one bucket; Python’s dict uses open addressing to resolve collisions and resizes when it gets too full. Sixty seconds on internals — try building the tiny chained hash table in our hashing tutorial — covers you for ninety percent of these questions.


The Highest-ROI Week of Your Prep

Hash maps won’t intimidate you the way dynamic programming does — and that’s precisely the point. A focused week here pays off in interviews more reliably than a month of DP grinding, because these problems actually show up, and because the three patterns above cover nearly all of them.

In the 60 Days of Algorithms challenge, hash tables and sets land on Day 23 and Day 24 — after arrays and linked lists, before the sorting and searching week — exactly where they belong in a real prep sequence. Sign up free and work through the whole syllabus one day at a time. If you’re mapping out your full timeline first, here’s how long FAANG prep actually takes.



Happy coding, and we’ll see you in the next lesson!