Solve 10 classic hashing interview problems — two sum, group anagrams, longest consecutive sequence, subarray sum equals k, top k frequent elements, valid anagram, isomorphic strings, word pattern, 4-sum count, and design a hashmap from scratch.
Problem 1 — Two Sum
def two_sum(nums, target):
"""
Find indices of two numbers that add to target.
Hash map: value → index. O(n) time, O(n) space.
"""
seen = {}
for i, num in enumerate(nums):
complement = target - num
if complement in seen:
return [seen[complement], i]
seen[num] = i
return []
print(two_sum([2,7,11,15], 9)) # [0, 1]
print(two_sum([3,2,4], 6)) # [1, 2]
Problem 3 — Longest Consecutive Sequence
def longest_consecutive(nums):
"""
Find length of longest consecutive sequence.
Hash set for O(1) membership. O(n) total.
Key insight: only start counting from the beginning of each sequence.
"""
num_set = set(nums)
max_length = 0
for n in num_set:
if n - 1 not in num_set: # n is the start of a sequence
length = 1
while n + length in num_set:
length += 1
max_length = max(max_length, length)
return max_length
print(longest_consecutive([100,4,200,1,3,2])) # 4 (1,2,3,4)
print(longest_consecutive([0,3,7,2,5,8,4,6,0,1])) # 9
Problem 4 — Subarray Sum Equals K
def subarray_sum(nums, k):
"""
Count subarrays with sum exactly k.
Prefix sum + hash map. O(n) time, O(n) space.
Key: prefix_sum[j] - prefix_sum[i] = k → looking for prefix_sum[i] = prefix_sum[j] - k
"""
count = 0
prefix_sum = 0
seen = {0: 1} # prefix_sum → count (init: sum=0 seen once)
for num in nums:
prefix_sum += num
count += seen.get(prefix_sum - k, 0)
seen[prefix_sum] = seen.get(prefix_sum, 0) + 1
return count
print(subarray_sum([1,1,1], 2)) # 2 — subarrays [1,1] at [0,1] and [1,2]
print(subarray_sum([1,-1,1], 1)) # 2
Problem 5 — Top K Frequent Elements
import heapq
from collections import Counter
def top_k_frequent(nums, k):
"""
Find k most frequent elements. O(n log k).
"""
freq = Counter(nums)
return heapq.nlargest(k, freq.keys(), key=lambda x: freq[x])
# Bucket sort approach — O(n):
def top_k_frequent_linear(nums, k):
freq = Counter(nums)
buckets = [[] for _ in range(len(nums) + 1)]
for num, count in freq.items():
buckets[count].append(num)
result = []
for i in range(len(buckets) - 1, 0, -1):
result.extend(buckets[i])
if len(result) >= k:
return result[:k]
return result
print(top_k_frequent([1,1,1,2,2,3], 2)) # [1, 2]
print(top_k_frequent([1], 1)) # [1]
Problem 6 — Valid Anagram
from collections import Counter
def is_anagram(s, t):
"""Check if t is an anagram of s. O(n)."""
if len(s) != len(t): return False
return Counter(s) == Counter(t)
# Manual counting approach:
def is_anagram_v2(s, t):
if len(s) != len(t): return False
count = {}
for c in s:
count[c] = count.get(c, 0) + 1
for c in t:
count[c] = count.get(c, 0) - 1
if count[c] < 0: return False
return True
print(is_anagram("anagram", "nagaram")) # True
print(is_anagram("rat", "car")) # False
Problem 7 — Isomorphic Strings
def is_isomorphic(s, t):
"""
Check if s and t have the same character-to-character mapping structure.
Each unique char in s maps to exactly one unique char in t.
O(n) time, O(1) space (bounded by alphabet size).
"""
if len(s) != len(t): return False
s_to_t = {}
t_to_s = {}
for cs, ct in zip(s, t):
if cs in s_to_t:
if s_to_t[cs] != ct: return False
else:
if ct in t_to_s: return False # ct already maps to different char
s_to_t[cs] = ct
t_to_s[ct] = cs
return True
print(is_isomorphic("egg", "add")) # True (e→a, g→d)
print(is_isomorphic("foo", "bar")) # False (o can't map to both a and r)
print(is_isomorphic("paper", "title")) # True
Problem 8 — Word Pattern
def word_pattern(pattern, s):
"""
Check if s follows the given pattern.
Similar to isomorphic strings but pattern is chars, s is words.
"""
words = s.split()
if len(pattern) != len(words): return False
char_to_word = {}
word_to_char = {}
for char, word in zip(pattern, words):
if char in char_to_word:
if char_to_word[char] != word: return False
else:
if word in word_to_char: return False
char_to_word[char] = word
word_to_char[word] = char
return True
print(word_pattern("abba", "dog cat cat dog")) # True
print(word_pattern("abba", "dog cat cat fish")) # False
print(word_pattern("aaaa", "dog cat cat dog")) # False
Problem 9 — 4-Sum Count (4-Array Version)
def four_sum_count(nums1, nums2, nums3, nums4):
"""
Count tuples (i,j,k,l) such that nums1[i]+nums2[j]+nums3[k]+nums4[l] == 0.
Split into two pairs, use hash map. O(n²) time and space.
"""
ab_sums = {}
for a in nums1:
for b in nums2:
ab_sums[a + b] = ab_sums.get(a + b, 0) + 1
count = 0
for c in nums3:
for d in nums4:
count += ab_sums.get(-(c + d), 0)
return count
print(four_sum_count([1,2],[-2,-1],[-1,2],[0,2])) # 2
Problem 10 — Design HashMap from Scratch
class MyHashMap:
"""
Implement a hash map without using any built-in hash table libraries.
Uses separate chaining.
"""
def __init__(self):
self.size = 1009 # Prime number for better distribution
self.buckets = [[] for _ in range(self.size)]
def _hash(self, key):
return key % self.size
def put(self, key: int, value: int) -> None:
idx = self._hash(key)
for i, (k, v) in enumerate(self.buckets[idx]):
if k == key:
self.buckets[idx][i] = (key, value)
return
self.buckets[idx].append((key, value))
def get(self, key: int) -> int:
idx = self._hash(key)
for k, v in self.buckets[idx]:
if k == key: return v
return -1
def remove(self, key: int) -> None:
idx = self._hash(key)
self.buckets[idx] = [(k, v) for k, v in self.buckets[idx] if k != key]
hm = MyHashMap()
hm.put(1, 1); hm.put(2, 2)
print(hm.get(1)) # 1
print(hm.get(3)) # -1
hm.put(2, 1)
print(hm.get(2)) # 1 (updated)
hm.remove(2)
print(hm.get(2)) # -1
Pattern Summary
| Problem | Hash Used | Key Insight |
|---|
| Two Sum | value → index | Complement lookup |
| Group Anagrams | sorted_str → group | Canonical form as key |
| Longest Consecutive | Set membership | Start from sequence beginnings |
| Subarray Sum = K | prefix_sum → count | Difference of prefix sums = k |
| Top K Frequent | freq counter + heap | Count then select |
| Valid Anagram | character counter | Equal frequencies |
| Isomorphic | bidirectional map | Two-way consistency check |
| Word Pattern | bidirectional map | Same as isomorphic |
| 4-Sum Count | pair_sum → count | Split problem in halves |
| Design HashMap | chaining | Prime size, linked list buckets |
The most powerful hashing pattern: Prefix sum + hash map solves a wide class of subarray problems in O(n): subarray sum = k, number of subarrays with equal 0s and 1s, longest subarray with given sum, etc.
*Hashing section complete. Next: Heaps — Heap Introduction*