9.1 Tries (Prefix Trees)
Understand what a Trie (Prefix Tree) is from scratch, why Hash Maps cannot do fast prefix autocomplete, and how to build and search a Trie.
1. What Is a Trie (Prefix Tree)?
A Trie (pronounced "try", from re-trie-val) is a tree where each step down a branch represents one character of a word.
Instead of storing whole strings like "cat", "car", and "cart" separately, a Trie shares their common starting letters (root -> 'c' -> 'a') and branches out only where the letters differ!
Each TrieNode holds just two things:
children: A dictionary (or 26-slot array for'a'..'z') mapping the next character to its childTrieNode.is_end: ATrue/Falseflag marking whether a complete word finishes at this exact node (for example, so you can tell if"car"was inserted as a full word and not just as the first 3 letters of"cart").
2. Why Do We Need a Trie Instead of a Hash Set?
Suppose you have a dictionary of 500,000 words and want to answer:
- Autocomplete / Prefix Check (
startsWith("app")): "Do ANY words in the dictionary begin with'app'?"- Hash Set: Must scan all
500,000words (O(N * L)), because hashing scrambles the whole word and loses prefix structure. - Trie: Just walk
3steps fromroot('a' -> 'p' -> 'p'). TakesO(L)time (3steps) regardless of whether the dictionary has10words or10 millionwords!
- Hash Set: Must scan all
- Board Pruning (
Word Search II): While backtracking on a 2D letter grid, if your current path is"qz"and'q'has no'z'child in the Trie, you can stop searching that branch immediately!
Interactive Step-by-Step Trace: Trie Prefix Sharing & Search
Insert "apple" and "app" into a Prefix Tree:
startsWith('app') is already True because the path a->p->p exists, even though search('app') is False.
3. Basic Operations & Walkthroughs (All 4 Languages)
class TrieNode:
def __init__(self):
self.children = {}
self.is_end = False
class Trie:
def __init__(self):
self.root = TrieNode()
# 1. Insert a word character by character -> O(L)
def insert(self, word: str) -> None:
curr = self.root
for ch in word:
if ch not in curr.children:
curr.children[ch] = TrieNode()
curr = curr.children[ch]
curr.is_end = True
# 2. Check if an EXACT word exists -> O(L)
def search(self, word: str) -> bool:
curr = self.root
for ch in word:
if ch not in curr.children:
return False
curr = curr.children[ch]
return curr.is_end
# 3. Check if ANY word starts with prefix -> O(L)
def startsWith(self, prefix: str) -> bool:
curr = self.root
for ch in prefix:
if ch not in curr.children:
return False
curr = curr.children[ch]
return True4. Core Interview Patterns & Common Traps
Use whenever you need to match multiple dictionary words simultaneously on a 2D board or support '.' wildcard character searches.
# Wildcard '.' Search in a Trie (Design Add and Search Words):
def dfs(idx: int, node: TrieNode) -> bool:
if idx == len(word):
return node.is_end
ch = word[idx]
if ch == ".":
return any(dfs(idx + 1, child) for child in node.children.values())
return ch in node.children and dfs(idx + 1, node.children[ch])| Operation / Pattern | Time | Space | Notes |
|---|---|---|---|
| Insert Word of Length L | O(L) | O(L) | Creates at most L new TrieNodes |
| Search Exact Word / Prefix | O(L) | O(1) | Independent of total words N in the Trie |