class Trie {root = new TrieNode();
insert(word: string) {let node = this.root;
for (const char of word) {if (!node.children.has(char)) node.children.set(char, new TrieNode());
node = node.children.get(char)!;
}
node.end = true;
}
search(word: string) {let node: TrieNode | undefined = this.root;
for (const char of word) node = node?.children.get(char);
return node !== undefined && node.end;
}
startsWith(prefix: string) {let node: TrieNode | undefined = this.root;
for (const char of prefix) node = node?.children.get(char);
return node ? node.collect(prefix) : []; // every end below, depth first
}
}
An empty trie: one root, no characters. 8 words will go in: cape, seat, arm, sea, car, sum, dot, cart.
Understanding Trie
The idea
A trie stores strings as paths. Every node holds one character and the words that share a prefix share the path of that prefix; a flag on a node says a word ends there. Inserting or looking up a word walks one node per character, so the cost is the length of the word, whatever the number of words stored.
Because a prefix is a node, everything under that node is the list of completions: startsWith walks the prefix and then collects the ends below it. That is why search boxes, spell checkers and IP routers keep their keys in tries, and why memory is the price: a node per character, with a map of children each.
The four stages
Complexity
Versus its siblings
Pseudocode
INSERT(word): node ← root; for each character: node ← its child for the character, created if missing; mark node as an end
SEARCH(word): node ← root; for each character: node ← its child, or fail; return whether node is an end
STARTSWITH(prefix): walk the prefix the same way, then collect every end below that node
When to use
Pitfalls
History
René de la Briandais described the structure in 1959 and Edward Fredkin named it in 1960, from retrieval, pronouncing it 'tree' to the confusion of everyone since. Radix trees and Patricia tries (Morrison, 1968) compress it, and it underlies most predictive text and routing tables today.