Pattern 01 of 20
Arrays & Hashing
Trade memory for speed: remember what you have seen in a hash map or set.
When to use it
- You need to find pairs, duplicates or counts in one pass
- A nested loop compares every element with every other
- Grouping items by a key (an anagram signature, a frequency)
The idea
Most brute-force array problems ask the same question again and again: have I seen this value before? A hash map answers it in constant time, so one pass with a lookup replaces a pass inside a pass.
Typical cost O(n) time, O(n) extra space.
How it works
Think of a hash map as a memory you can query in constant time. Almost every brute-force array solution repeats one expensive question inside a loop: is there an earlier element that completes this one? Storing what you have already seen turns that inner scan into a single lookup.
It works because the question can be phrased as an exact key. Pair sums become 'have I seen target minus x', duplicates become 'is x already in the set', and grouping becomes 'which bucket does this key belong to'. Once the question is a key, the hash structure answers it without caring how many items came before.
The pattern hides behind words like count, group, frequency, first unique, or 'in one pass'. It also hides in prefix sums: a subarray summing to k is just two prefix sums that differ by k, which is the pair-sum lookup again.
The real skill is choosing the key. A sorted string, a tuple of character counts, a running sum, a remainder modulo k: the right signature collapses many different-looking inputs into one bucket.
How to attack it
- 1Write the brute force and name the question the inner loop keeps asking.
- 2Rephrase that question as a lookup on an exact key.
- 3Decide what the map stores per key: a boolean, a count, an index, or a list.
- 4Decide the order: look up before inserting the current element, or after.
- 5Handle the empty input and repeated values, then confirm one pass is enough.
Template
A skeleton to adapt, not a solution to paste. Write it from memory a few times.
def one_pass_lookup(nums, target):
# Maps a value (or a derived key) to what we need later: here, its index.
seen = {}
for i, x in enumerate(nums):
need = target - x # the key that would complete the current element
if need in seen:
return [seen[need], i]
# Insert AFTER the lookup so an element never pairs with itself.
seen[x] = i
return []
def group_by_signature(items):
groups = {}
for item in items:
key = "".join(sorted(item)) # choose a signature shared by the group
groups.setdefault(key, []).append(item)
return list(groups.values())Variants
- Complement lookup
- Store each value and look up the partner that would complete it. The base of every pair-sum question.
- Frequency counting
- Count occurrences, then read the counts: duplicates, majority element, first unique character, anagram checks.
- Grouping by signature
- Map each item to a canonical key so equivalent items land in one bucket. The key design is the whole problem.
- Prefix sum plus map
- Store how often each running sum has occurred; a subarray summing to k exists when sum minus k was seen before. Seed the map with sum 0.
- Bucket by value range
- When values are bounded, an array indexed by value replaces the map and makes counting sort or top-k by frequency linear.
Pitfalls
- Inserting the current element before looking it up, so it pairs with itself.
- Forgetting to seed a prefix-sum map with {0: 1}, which misses subarrays that start at index 0.
- Using a mutable list as a dictionary key in Python, or an array as a Map key in JavaScript (compared by reference, never equal).
- Overwriting an index on duplicates when the problem needs the first occurrence.
- Assuming the map's iteration order is sorted; it is insertion order at best.
What it costs
| Operation | Cost |
|---|---|
| Hash map insert / lookup | O(1) average, O(n) worst case |
| One pass with lookups | O(n) time, O(n) space |
| Grouping by sorted-string key | O(n · k log k) for n strings of length k |
| Grouping by count tuple | O(n · k) with a fixed alphabet |
| Brute force it replaces | O(n²) time, O(1) space |
61 Arrays & Hashing problems
Easiest first. Problems marked Free can be solved without Pro.
- 1Pair With Target SumFreeEasy
- 2Duplicate Badge ScanFreeEasy
- 3Same Letters, New OrderFreeEasy
- 4First Lone CharacterEasy
- 5Running Ledger TotalsEasy
- 6Balance Point IndexEasy
- 7Letter Cipher ConsistencyEasy
- 8Clip Letters Into a NoteEasy
- 9Shared Values, SortedEasy
- 10Majority Ballot WinnerEasy
- 11Thin Out a Sorted LogEasy
- 12Merge Into the Reserved TailEasy
- 13The Missing BadgeEasy
- 14Sorted Before the TurnEasy
- 15Expand Count-Value PairsEasy
- 16Biggest Value AheadEasy
- 17The Doubled and the LostEasy
- 18Run-Length LabelEasy
- 19Unchain the XOR LogEasy
- 20Every Vanished NumberEasy
- 21Bundle Anagram FamiliesMedium
- 22Most Frequent Values, RankedMedium
- 23Product of All OthersMedium
- 24Longest Unbroken RunMedium
- 25Count Windows Summing to KMedium
- 26Range Sum QueriesMedium
- 27Longest Balanced Binary StretchMedium
- 28Count Sums Divisible by KMedium
- 29Sudoku Board AuditMedium
- 30Four Lists to ZeroMedium
- 31Stretch Summing to a MultipleMedium
- 32Longest Stretch Summing to KMedium
- 33Order Letters by FrequencyMedium
- 34Fewest Bricks CrossedMedium
- 35Shortest Trim for a Divisible TotalMedium
- 36Rotate the CarouselMedium
- 37Riffle the Deck in PlaceMedium
- 38Alternate Gains and LossesMedium
- 39Over a Third of the VoteMedium
- 40Unpack a Length-Prefixed StreamMedium
- 41Split an Escaped RecordMedium
- 42The Repeated TicketMedium
- 43Letter at Position K of the TapeMedium
- 44Values Seen TwiceMedium
- 45Values Already HomeMedium
- 46Smallest Left Block Without CrossingsMedium
- 47Splits That Share a LeaderMedium
- 48Follow the Index TableMedium
- 49Fewest Swaps to Order a PermutationMedium
- 50Smallest Absent PositiveHard
- 51Grid Blocks Summing to TargetHard
- 52Palindrome-Ready SubstringsHard
- 53Median-Anchored StretchesHard
- 54Palindrome Word PairsHard
- 55Chunk Concatenation StartsHard
- 56Earliest Smallest RotationHard
- 57Most Blocks That Sort IndependentlyHard
- 58Best Rotation for the ScoreboardHard
- 59Widest Gap in Sorted OrderHard
- 60Seat Every Pair TogetherHard
- 61Smaller Values DownstreamHard
Solve them in the browser, in Python or JavaScript.