Introduction#
A hash table makes “have I seen this, and how many times?” O(1). Most problems just replace an O(n) linear search with a hash lookup; the key is choosing the right key:
| Idea | The key | Examples |
|---|---|---|
| Counting | The value | Counts divisible by k, odd counts |
| Frequency of frequencies | The count | First element with a unique count |
| The array as a hash table | index = value - 1 | First missing positive, duplicates |
| Several keys | Row, column, r - c, r + c | Cells lit by lamps |
| Normalized key | Sorted string, prefix | Anagrams, same first k characters |
Template 1: Counting and frequency of frequencies#
from collections import Counter
cnt = Counter(nums) # value -> count
freq_of_freq = Counter(cnt.values()) # count -> how many values have it
for x in nums: # first one from the left
if freq_of_freq[cnt[x]] == 1:
return x
- Sum of elements whose count is divisible by
k(LC 3712):sum(x * c for x, c in cnt.items() if c % k == 0). - First element with a unique count (LC 3843): the code above.
- Toggling bulbs (LC 3842): bulbs toggled an odd number of times stay on; toggle in a set or count parity, then sort.
- Groups with the same first
kcharacters (LC 3839): the key isword[:k], and words shorter thankdo not count. - Three-digit even numbers from given digits (LC 2094): enumerate the even numbers 100 to 998 instead and check that the digits needed are available.
Countersubtraction keeps only positives, sonot (need - have)means “enough”. - Missing numbers in a range (LC 3731): convert to a set first so lookups are
O(1).
Template 2: The array itself as a hash table#
When values lie in [1, n], the sign of nums[v - 1] can record “v was seen” with no extra space:
for x in nums:
i = abs(x) - 1 # the value may already be negated
if nums[i] < 0:
... # i + 1 appears a second time
else:
nums[i] = -nums[i]
- Numbers appearing twice (LC 442): the code above.
- Missing numbers (LC 448): after marking everything, each
istill positive meansi + 1never appeared. - First missing positive (LC 41): the answer is in
1..n + 1. Replace values<= 0withn + 1(not 0: negating 0 gives 0, so it cannot be marked), mark1..n, and the first positionistill positive gives the answeri + 1.
Extension: complement lookups, run starts, voting#
- Two Sum (LC 1): store “value → index”, checking
target - xbefore storingx, so no element pairs with itself. - Longest consecutive sequence (LC 128): put everything in a set and count upward only from starts whose
x - 1is absent, inO(n). - Majority element (LC 169, 229): Boyer-Moore voting, where different values cancel; above
n / 3, keep two candidates and recount at the end.
Template 3: Counting several keys at once#
A lamp lights its row, column, and both diagonals. Keep a counter for each; cells on one diagonal share r - c and on the anti-diagonal share r + c:
row, col, diag, anti = Counter(), Counter(), Counter(), Counter()
on = set()
for r, c in lamps:
if (r, c) not in on: # the same lamp can repeat
on.add((r, c))
row[r] += 1; col[c] += 1; diag[r - c] += 1; anti[r + c] += 1
lit = row[qr] or col[qc] or diag[qr - qc] or anti[qr + qc] # is query (qr, qc) lit?
Turning a lamp off decrements all four counters, and only for lamps that are actually on (LC 1001).
Template 4: Fixed left end, incremental counts#
With a small n, fix the left end and extend right; each new number updates O(1) counts:
for i in range(n):
cnt, freq = {}, Counter() # value -> count, count -> how many
for j in range(i, n):
c = cnt.get(nums[j], 0)
if c:
freq[c] -= 1
if freq[c] == 0:
del freq[c] # delete so len(freq) is the number of distinct counts
cnt[nums[j]] = c + 1
freq[c + 1] += 1
- Only counts
fand2f(LC 3960):len(freq) == 2 and max(freq) == 2 * min(freq), or a single value.
Template 5: Normalize, then use as the key#
- Anagrams (LC 2273): equal
sorted(word)means anagrams. Repeatedly deleting a word that is an anagram of the previous one is the same as keeping only words that are not anagrams of the last kept word. - Custom sort (LC 3606): filter the valid ones, then sort by
(category order, code). - Grouping (LC 49): use
"".join(sorted(s))as the key in adefaultdict(list).set(s)does not work:"bb"and"b"share a set. - Bijection (LC 205, 290):
len(set(a)) == len(set(b)) == len(set(zip(a, b))). When encoding with first-occurrence indices, separate the numbers.
Counter Arithmetic#
| Expression | Meaning | Examples |
|---|---|---|
not (Counter(a) - Counter(b)) | b covers every character of a | LC 383, 1160, 748 |
c1 & c2 | per-character minimum | LC 1002 |
min(cnt[c] // need[c] for c in need) | how many copies can be built | LC 1189 |
Key Parameters#
- Looking up a missing key in a
Counterreturns 0 without inserting it; adefaultdictlookup inserts. - Delete a count when it reaches 0 so
len()is the number of kinds.
Complexity#
Building is O(n); each lookup is O(1) on average.
Common Mistakes#
These are mistakes I actually made while practicing:
- Using
inon a list:O(n)each time,O(n²)overall, TLE (LC 442). - Using 0 as the “impossible” value: negating 0 gives 0, so it cannot be marked, and with everything present the answer is
n + 1(LC 41). - The wrong counter after copy-paste: querying
row_count[col]and decrementingrow_count[nc]when turning off, so the column counts were never right (LC 1001). - Looking up with the wrong key: checking a count against the “value -> count” table (LC 3960).
- The wrong variable in a repeat check: with
j == kthe check should becounter[j]; I wrotecounter[i](LC 2094). - Forgetting to sort: a set converted to a list has no order (LC 3842).
- Counting words that are too short:
word[:k]shorter thankcannot form a group (LC 3839). - A character range missing its last letter:
range(ord("a"), ord("z"))excludesz; I also had the sort key in the wrong order (LC 3606). d.get(x, 0) + 1without storing it: the map stays empty (LC 242).- Checking only that the counts sum to 0: positives and negatives cancel; check each one (LC 242).
- Reading a
Counterdifference backwards: a non-empty difference means a shortfall (LC 383). - Using
setfor anagrams or common characters: it drops multiplicity (LC 49, 1002). - Storing and reading keys in different cases: store
B, look upb, never found (LC 748, 3121). - Turning a list into a
setbefore searching: order is lost, so “earliest on ties” is impossible (LC 748). - Index encodings without a separator:
1+12and11+2both read112(LC 205). - Filtering by raw count before dividing: when
lis needed twice, comparecnt // 2(LC 1189). - Finding indices with
nums.index(x)in Two Sum: equal values get the same index (LC 1). - Inverted conditions:
len(set(nums)) == len(nums)means “no duplicate” (LC 217); two limits needmin, notmax(LC 575). - Taking only
most_common()[0]: several values may share the highest count (LC 697). - Ordering leftovers by frequency: they go in ascending order, keeping multiplicities (LC 1122).
- Lists as dict keys: use tuples, normalized in order first (LC 1128).
- Overwriting the length and ignoring duplicates when scanning sorted runs (LC 128).
round(n / 2)uses banker’s rounding in Python 3: take the middle withn // 2(LC 169).
Related LeetCode#
LC 41First Missing PositiveLC 442Find All Duplicates in an ArrayLC 1001Grid IlluminationLC 3712Sum of Elements With Frequency Divisible by KLC 3843First Element with Unique FrequencyLC 3960Frequency Balance SubarrayLC 2094Finding 3-Digit Even NumbersLC 3731Find Missing ElementsLC 3842Toggle Light BulbsLC 3839Number of Prefix Connected GroupsLC 3606Coupon Code ValidatorLC 2273Find Resultant Array After Removing AnagramsLC 594Longest Harmonious SubsequenceLC 219Contains Duplicate IILC 187Repeated DNA SequencesLC 242Valid AnagramLC 49Group AnagramsLC 383Ransom NoteLC 1189Maximum Number of BalloonsLC 1160Find Words That Can Be Formed by CharactersLC 1002Find Common CharactersLC 771Jewels and StonesLC 884Uncommon Words from Two SentencesLC 819Most Common WordLC 409Longest PalindromeLC 3120Count the Number of Special Characters ILC 3121Count the Number of Special Characters IILC 748Shortest Completing WordLC 290Word PatternLC 205Isomorphic StringsLC 1Two SumLC 217Contains DuplicateLC 575Distribute CandiesLC 169Majority ElementLC 229Majority Element IILC 961N-Repeated Element in Size 2N ArrayLC 448Find All Numbers Disappeared in an ArrayLC 128Longest Consecutive SequenceLC 1122Relative Sort ArrayLC 697Degree of an ArrayLC 1128Number of Equivalent Domino Pairs
