Overview
A trie (prefix tree) stores strings character by character along paths from the root. Each node represents a prefix, has children for the next possible characters, and a flag marking whether a complete word ends there. Words that share a prefix share the same path.
Insert and search take O(L), where L is the word length, independent of how many words are stored. The real strength is prefix queries: finding all words that start with 'app' means walking three nodes and then collecting that subtree, which a hash set cannot do efficiently.
As you type 'D', 'a', 'r', the list narrows to names starting with those letters. Each typed letter moves one step down a shared path; you never scan every contact.
When to use it
- Autocomplete and 'starts with' queries.
- Many words must be searched in a grid or text at once (word search II).
- Longest common prefix, or the shortest unique prefix of words.
- Wildcard search where '.' matches any character.
- Maximum XOR of two numbers (a binary trie over bits).
Problem patterns it solves
Recognize it when: starts with, autocomplete, suggestions.
- 208. Implement Trie (Prefix Tree)
- 1268. Search Suggestions System
- 14. Longest Common Prefix
Recognize it when: '.' matches any letter; DFS through all children at wildcards.
- 211. Design Add and Search Words Data Structure
Recognize it when: find every dictionary word on a board; prune DFS by prefix.
- 212. Word Search II
Recognize it when: replace words by their shortest dictionary prefix.
- 648. Replace Words
- 720. Longest Word in Dictionary
Recognize it when: maximize XOR by choosing opposite bits greedily.
- 421. Maximum XOR of Two Numbers in an Array
- 1707. Maximum XOR With an Element From Array
Where it is used in real software
Search engines and IDEs suggest completions by walking a prefix tree (often compressed and ranked by popularity).
Routers find the longest matching network prefix for a destination IP with binary tries (Patricia or radix trees).
Dictionaries stored as tries allow fast lookups and nearby-word suggestions.
HTTP routers in Go (httprouter) and Node.js (find-my-way used by Fastify) match URL paths with radix trees.
Key terms
- Node
- Holds children (map or array of 26) and an isEnd flag.
- Prefix
- The path from the root to a node.
- isEnd
- True when a stored word ends at this node.
- Radix / compressed trie
- Merges single-child chains into one edge labeled with a string.
How it works, step by step
- 1Insert
For each character, create the child if missing and move to it. Mark isEnd at the last node.
- 2Search a word
Follow characters; if any child is missing return false; at the end return node.isEnd.
- 3startsWith
Same walk, but return true if the path exists, regardless of isEnd.
- 4Collect words under a prefix
Walk to the prefix node, then DFS its subtree, building strings.
Insert "app", "apple", "apt", then query
Root -> a -> p -> p (end) -> l -> e (end); p -> t (end)
| Query | Path walked | Result | Why |
|---|---|---|---|
| search("app") | a, p, p | true | isEnd is set at the second p |
| search("ap") | a, p | false | path exists but no word ends there |
| startsWith("ap") | a, p | true | path exists |
| search("apple") | a, p, p, l, e | true | isEnd at e |
| search("apx") | a, p, x missing | false | no child x |
NOWQuery: search("app") | Path walked: a, p, p | Result: true | Why: isEnd is set at the second p
All three words share the nodes a and p. Every query costs only the length of the query string.
Implementation
class TrieNode { constructor() { this.children = new Map(); this.isEnd = false; }} class Trie { constructor() { this.root = new TrieNode(); } insert(word) { let node = this.root; for (const ch of word) { if (!node.children.has(ch)) node.children.set(ch, new TrieNode()); node = node.children.get(ch); } node.isEnd = true; } #walk(prefix) { let node = this.root; for (const ch of prefix) { node = node.children.get(ch); if (!node) return null; } return node; } search(word) { return this.#walk(word)?.isEnd === true; } startsWith(prefix) { return this.#walk(prefix) !== null; } suggestions(prefix, limit = 3) { const start = this.#walk(prefix); const result = []; const dfs = (node, path) => { if (result.length === limit) return; if (node.isEnd) result.push(path); for (const ch of [...node.children.keys()].sort()) dfs(node.children.get(ch), path + ch); }; if (start) dfs(start, prefix); return result; }} // 211: '.' matches any characterfunction searchWithDots(node, word, i = 0) { if (i === word.length) return node.isEnd; if (word[i] === ".") { for (const child of node.children.values()) if (searchWithDots(child, word, i + 1)) return true; return false; } const child = node.children.get(word[i]); return child ? searchWithDots(child, word, i + 1) : false;}Complexity and performance
L = length of the word or prefix.
Worst case N words x L characters nodes.
Fast but memory-heavy; Map is compact.
Trade-offs
A hash set checks whole words in O(L) too, but cannot answer prefix queries without scanning all words.
Arrays of 26 are fastest for lowercase English; maps save memory for large or sparse alphabets.
Tries can use much more memory than the strings themselves; radix trees compress chains to reduce it.
Variants and related techniques
Edges hold substrings; used in routers and HTTP routing.
Each node has children 0 and 1 for bits; answers maximum XOR queries in O(32).
Stores all suffixes of a string for fast substring search.
Store how many words pass through each node to count words with a prefix.
Common mistakes
- Returning true for search on a prefix that is not a word.
Fix: Check isEnd at the final node.
- Duplicate results in Word Search II.
Fix: Clear the stored word after finding it.
- Not pruning.
Fix: Stop DFS as soon as the current prefix has no trie node.
Interview questions
Why use a trie over a hash set for autocomplete?
A trie walks to the prefix in O(L) and only explores matching words. A hash set would have to check every stored word for the prefix.
How does a trie speed up Word Search II?
Instead of running a separate search for each word, one DFS over the board follows trie nodes, and stops immediately when the current path is not a prefix of any word.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| 208. Implement Trie (Prefix Tree) | Medium | Core operations. |
| 211. Design Add and Search Words Data Structure | Medium | Wildcard DFS. |
| 648. Replace Words | Medium | Shortest root prefix. |
| 1268. Search Suggestions System | Medium | Sorted suggestions. |
| 421. Maximum XOR of Two Numbers in an Array | Medium | Binary trie. |
| 212. Word Search II | Hard | Trie-guided backtracking. |