Dictionary Interview Recipes
Introduction
Dictionaries are the primary tool for optimizing algorithms. Memorizing these dictionary-based recipes will allow you to quickly reduce \(O(N^2)\) algorithms to \(O(N)\).
Recipe 1: Two Sum (Trading Space for Time)
If a problem asks you to find two numbers in an array that add up to a target, use a dictionary to store the numbers you have seen so far.
def two_sum(nums, target):
# Map: value -> index
seen = {}
for i, num in enumerate(nums):
complement = target - num
if complement in seen:
return [seen[complement], i]
seen[num] = i
return []
Recipe 2: Grouping Elements (Anagrams)
When asked to group similar elements together (like grouping strings that are anagrams of each other), use a dictionary where the key is a normalized version of the element, and the value is a list of matching elements.
import collections
def group_anagrams(strs):
# Map: sorted_tuple -> list of original strings
groups = collections.defaultdict(list)
for s in strs:
# Normalize the string (sort it)
# Must be a tuple because lists are not hashable!
key = tuple(sorted(s))
groups[key].append(s)
return list(groups.values())
Recipe 3: Memoization (Caching Results)
In Dynamic Programming or recursive tree traversals, use a dictionary to cache the results of expensive function calls to avoid recalculating them.
def fibonacci(n, memo=None):
if memo is None:
memo = {}
if n in memo:
return memo[n]
if n <= 1:
return n
# Compute and store in memo before returning
memo[n] = fibonacci(n-1, memo) + fibonacci(n-2, memo)
return memo[n]
@functools.lru_cache which does this automatically, but interviewers often want to see you implement it manually).
Recipe 4: Frequency Counting
When asked to find the most common element or check if two arrays are permutations, use collections.Counter.
from collections import Counter
def top_k_frequent(nums, k):
counts = Counter(nums)
# most_common(k) returns a list of (element, frequency)
return [elem for elem, freq in counts.most_common(k)]
Summary
- Two Sum Pattern: Store
seen[value] = indexwhile iterating. - Grouping: Use a normalized, immutable key (like a sorted tuple) to group items in a
defaultdict(list). - Memoization: Pass a dictionary through recursive calls to cache
memo[state] = result. - Top K: Use
Counter(nums).most_common(k).