Trie (Prefix Tree)
Strings that share prefixes share nodes. One traversal of m characters answers any prefix query in O(m) — regardless of how many words are in the dictionary. HashSet can't do that.
HashSet: exact match in O(m) but prefix query = O(n·m). Trie: prefix query in O(m) — n doesn't matter.
The Trie's secret: shared prefixes share nodes. "cat", "car", "card", "care" all share the nodes c→a. Insert all four words and you create 6 nodes, not 14 characters.
TrieNode: children + is_end
Two fields. That's the entire data structure.
childrenMaps each character to the next node. Up to 26 entries for lowercase English. Use {} (empty dict) not a 26-element array — sparser and simpler in Python.
if ch not in node.children:
node.children[ch] = TrieNode()
node = node.children[ch]is_endTrue when a complete word ends at this node. Multiple words can share a node — only the ones that end here have is_end=True. This distinguishes 'car' from 'card'.
# At end of insert: node.is_end = True # In search: return node.is_end
search: return node.is_end — must be a complete wordstarts_with: return True — any continuation countsThe 3-Step Framework
Insert all, then query any prefix in O(m).
For each word, walk from root character by character. If a node for the character doesn't exist, create it. After processing the last character, set is_end=True. Time: O(total characters inserted).
node = self.root
for ch in word:
if ch not in node.children:
node.children[ch] = TrieNode()
node = node.children[ch]
node.is_end = TrueFor any query (search, starts_with, suggestions, index pairs): walk from root following the query characters. If any character is missing, the prefix/word doesn't exist — return False/None immediately.
node = self.root
for ch in prefix:
if ch not in node.children:
return None # prefix absent
node = node.children[ch]
return node # the prefix nodeFor autocomplete/suggestions: DFS from the prefix node. Accumulate the path suffix. When is_end=True, add current_prefix+suffix to results. Iterate sorted(node.children) for lexicographic order.
def collect(node, prefix, results, limit=3):
if len(results) >= limit: return
if node.is_end:
results.append(prefix)
for ch in sorted(node.children):
collect(node.children[ch],
prefix+ch, results, limit)Pattern Recognition
'prefix', 'autocomplete', 'dictionary lookup', 'wildcard' → Trie.