Pattern #46
Trie / Prefix Tree
Important interview questions, thinking patterns, Character Tree prefix sharing, Search vs StartsWith distinction, Bit Trie, and Go code templates.
Must Solve
15 core questions — solve these first.
- 1.Implement TrieRoot node with [26]*TrieNode & isEnd bool; Insert, Search, StartsWith methodsmedium
- 2.Design Add and Search Words Data StructureTrie + DFS: when wildcard "." is encountered, iterate all non-nil childrenmedium
- 3.Replace WordsInsert root dictionary words into Trie; walk sentence word until isEnd == truemedium
- 4.Longest Common Prefix Using TrieInsert words into Trie; walk root while node has exactly 1 child & isEnd == falseeasy
- 5.Search Suggestions SystemTrie + top 3 suggestions cached at each node during insertionmedium
- 6.Word Search IITrie + Grid Backtracking: early prune DFS branch as soon as grid prefix is absent from Triehard
- 7.Map Sum PairsTrie node storing sum value; Search prefix returns node.Sum directlymedium
- 8.Longest Word in DictionaryInsert all words; BFS/DFS from Trie root considering only nodes with isEnd == truemedium
- 9.Maximum XOR of Two Numbers in an ArrayBit Trie (32-bit binary paths): for bit b, prefer opposite child 1-b to maximize XORmedium
- 10.Stream of CharactersReverse Trie: insert words backwards; query checks current character stream in reversehard
- 11.Prefix and Suffix SearchInsert combined string suffix + "{" + word into Trie to query in O(L)hard
- 12.Concatenated WordsTrie + DP/DFS: test if word can be formed by >= 2 dictionary wordshard
- 13.Word Break with TrieInsert dictionary into Trie; DP[i] checks if substring s[j:i] exists in Triemedium
- 14.Count Words With Given PrefixWalk Trie to prefix node and return node.PrefixCounteasy
- 15.Implement AutocompleteWalk Trie to prefix node + DFS below to collect completionsmedium
Also Important
9 more questions worth practicing.
- 16.Short Encoding of WordsReverse Trie (Suffix Tree) discarding words that are suffixes of longer wordsmedium
- 17.Palindrome PairsTrie storing reversed words + checking remaining prefix/suffix for palindromehard
- 18.Magic DictionaryTrie + DFS allowing exactly 1 character mismatchmedium
- 19.Word SquaresTrie prefix lookup + Backtracking to construct N x N word matrixhard
- 20.Design File SystemTrie path hierarchy: split path by "/" and traverse folder nodesmedium
- 21.Count Distinct Substrings Using TrieInsert all suffixes of string into Trie; total distinct substrings = total Trie nodes - 1medium
- 22.Maximum XOR With an Element From ArrayOffline queries sorted by limit + Bit Trie insertionhard
- 23.Phone Directory / Contact SearchTrie storing contact names + returning suggestions for each typed digitmedium
- 24.IP Routing Prefix MatchingBinary Trie matching longest subnet mask prefix (e.g. 10.1.4.*/24)medium
How to Think
- Many words share prefixes?Trie / Prefix Tree
- Need startsWith(prefix)?Trie
- Need autocomplete?Trie + DFS from prefix node
- Need dictionary search while walking a board?Trie + Backtracking (Word Search II)
- Need maximum XOR?Bit Trie
Go Trie Code Template
Complete Trie Implementation in Go
type TrieNode struct {
Children [26]*TrieNode
IsEnd bool
}
type Trie struct {
Root *TrieNode
}
func Constructor() Trie {
return Trie{Root: &TrieNode{}}
}
// 1. Insert: O(L) time
func (t *Trie) Insert(word string) {
curr := t.Root
for i := 0; i < len(word); i++ {
idx := word[i] - 'a'
if curr.Children[idx] == nil {
curr.Children[idx] = &TrieNode{}
}
curr = curr.Children[idx]
}
curr.IsEnd = true
}
// 2. Search: O(L) time (Path exists AND isEnd == true)
func (t *Trie) Search(word string) bool {
curr := t.Root
for i := 0; i < len(word); i++ {
idx := word[i] - 'a'
if curr.Children[idx] == nil {
return false
}
curr = curr.Children[idx]
}
return curr.IsEnd
}
// 3. StartsWith: O(L) time (Path exists ONLY)
func (t *Trie) StartsWith(prefix string) bool {
curr := t.Root
for i := 0; i < len(prefix); i++ {
idx := prefix[i] - 'a'
if curr.Children[idx] == nil {
return false
}
curr = curr.Children[idx]
}
return true
}👉 Total Time: O(L) per operation | Space: O(N · L) per word set
Search vs StartsWith & Autocomplete
1. Search(word): Traverses character path and requires curr.IsEnd == true at the final node. Searching for "app" when only "apple" was inserted returns false.
2. StartsWith(prefix): Only requires character path to exist! Returns true regardless of whether IsEnd is true.
3. Autocomplete: Walk to the prefix node in O(L) time, then run DFS below that node to collect all valid word completions!
Word Search II & Bit Trie (Max XOR)
1. Word Search II (Trie + Grid Backtracking): Insert all dictionary words into a Trie. When exploring grid paths in DFS, check if the current letter sequence exists as a Trie node. If missing, prune the DFS branch immediately!
2. Bit Trie (Maximum XOR): Insert 32-bit binary paths (0 or 1) from MSB to LSB. To maximize XOR for a number X, prefer the opposite bit child (1 - bit) at each level because 0 ^ 1 = 1!
Trie → Character Tree sharing word prefixes
Search → Character path exists AND isEnd == true
StartsWith → Character path exists ONLY
Autocomplete → Walk to prefix node + DFS below
Word Search II → Trie + Grid Backtracking (Prune dead prefixes!)
Maximum XOR → Bit Trie preferring opposite bit child (1 - bit)💡 Golden Rule: "A Trie turns every prefix into a reusable path, so many words can share the same beginning instead of storing that beginning again and again."
Common Interview Mistakes
Path existence alone does NOT mean a complete word exists! Always set isEnd = true on insertion and check it during search.
If you only need single exact word lookups without prefix queries, a simple Hash Set is faster and uses less memory!
The primary power of using a Trie in Word Search II is stopping grid DFS immediately when a grid path forms an invalid Trie prefix.
When deleting "care" when "car" exists, do NOT remove nodes for 'c', 'a', 'r'! Only unmark or remove unshared leaf nodes.
Interview Rules
- 1. Many words share prefixes / startsWith needed? → Trie / Prefix Tree
- 2. Search vs StartsWith? → Search = path +
isEnd; StartsWith = path only - 3. Time Complexity per operation? →
O(L)where L is word/prefix length - 4. Autocomplete / Search Suggestions? → Walk to prefix node + DFS below
- 5. Word Search II (Dictionary + Grid)? → Insert dictionary into Trie + Grid Backtracking with prefix pruning
- 6. Maximum XOR problem? → Bit Trie storing 32-bit binary paths
- 7. Child Representation? → Fixed
a-zuses[26]*TrieNodearray; flexible Unicode usesmap[rune]*TrieNode - 8. Overall Complexity → Time:
O(L)| Space:O(N · L)
Small Rules
- Rule 1: Trie stores prefixes naturally along character branches.
- Rule 2: Each character moves down exactly one level in the Trie.
- Rule 3: Search requires path existence and isEnd == true.
- Rule 4: StartsWith requires character path existence only.
- Rule 5: Bit Tries evaluate binary bits from MSB to LSB to maximize XOR.
Production Thinking
Search Engine Query Autocomplete → Instantly suggesting search queries (iph -> iphone case) as user types
Contact List Prefix Search → Narrowing contact search (Ali -> Alice, Alina) in mobile contact apps
IP Router Longest Prefix Match → Matching IP routing subnet masks (10.1.4.*/24) using binary Radix/Patricia tries
Dictionary Spell Checking → Verifying word validity & generating spelling correction candidates
Remember This
Trie → Prefix Tree
Character → One level
Insert → Create missing children
Search → Path + isEnd
Prefix → Path only
Autocomplete → Prefix node + DFS
Word Search II → Trie + Backtracking
Maximum XOR → Bit Trie
Time → O(word length)
Main cost → Memory💡 Golden Rule: "A Trie turns every prefix into a reusable path, so many words can share the same beginning instead of storing that beginning again and again."