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. 1.Implement Trie
    medium
  2. 2.Design Add and Search Words Data Structure
    medium
  3. 3.Replace Words
    medium
  4. 4.Longest Common Prefix Using Trie
    easy
  5. 5.Search Suggestions System
    medium
  6. 6.Word Search II
    hard
  7. 7.Map Sum Pairs
    medium
  8. 8.Longest Word in Dictionary
    medium
  9. 9.Maximum XOR of Two Numbers in an Array
    medium
  10. 10.Stream of Characters
    hard
  11. 11.Prefix and Suffix Search
    hard
  12. 12.Concatenated Words
    hard
  13. 13.Word Break with Trie
    medium
  14. 14.Count Words With Given Prefix
    easy
  15. 15.Implement Autocomplete
    medium

Also Important

9 more questions worth practicing.

  1. 16.Short Encoding of Words
    medium
  2. 17.Palindrome Pairs
    hard
  3. 18.Magic Dictionary
    medium
  4. 19.Word Squares
    hard
  5. 20.Design File System
    medium
  6. 21.Count Distinct Substrings Using Trie
    medium
  7. 22.Maximum XOR With an Element From Array
    hard
  8. 23.Phone Directory / Contact Search
    medium
  9. 24.IP Routing Prefix Matching
    medium

How to Think

  1. Many words share prefixes?Trie / Prefix Tree
  2. Need startsWith(prefix)?Trie
  3. Need autocomplete?Trie + DFS from prefix node
  4. Need dictionary search while walking a board?Trie + Backtracking (Word Search II)
  5. 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!

Visual Memory Rule
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

1. Forgetting isEnd Flag

Path existence alone does NOT mean a complete word exists! Always set isEnd = true on insertion and check it during search.

2. Using Trie for Single Exact Lookups

If you only need single exact word lookups without prefix queries, a simple Hash Set is faster and uses less memory!

3. Word Search II Without Prefix Pruning

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.

4. Deleting Shared Prefixes Incorrectly

When deleting "care" when "car" exists, do NOT remove nodes for 'c', 'a', 'r'! Only unmark or remove unshared leaf nodes.

Interview Rules

  1. 1. Many words share prefixes / startsWith needed? → Trie / Prefix Tree
  2. 2. Search vs StartsWith? → Search = path + isEnd; StartsWith = path only
  3. 3. Time Complexity per operation?O(L) where L is word/prefix length
  4. 4. Autocomplete / Search Suggestions? → Walk to prefix node + DFS below
  5. 5. Word Search II (Dictionary + Grid)? → Insert dictionary into Trie + Grid Backtracking with prefix pruning
  6. 6. Maximum XOR problem? → Bit Trie storing 32-bit binary paths
  7. 7. Child Representation? → Fixed a-z uses [26]*TrieNode array; flexible Unicode uses map[rune]*TrieNode
  8. 8. Overall Complexity → Time: O(L) | Space: O(N · L)

Small Rules

  1. Rule 1: Trie stores prefixes naturally along character branches.
  2. Rule 2: Each character moves down exactly one level in the Trie.
  3. Rule 3: Search requires path existence and isEnd == true.
  4. Rule 4: StartsWith requires character path existence only.
  5. Rule 5: Bit Tries evaluate binary bits from MSB to LSB to maximize XOR.

Production Thinking

Search Engine Query AutocompleteInstantly suggesting search queries (iph -> iphone case) as user types

Contact List Prefix SearchNarrowing contact search (Ali -> Alice, Alina) in mobile contact apps

IP Router Longest Prefix MatchMatching IP routing subnet masks (10.1.4.*/24) using binary Radix/Patricia tries

Dictionary Spell CheckingVerifying 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."