A trie (prefix tree) stores strings character-by-character — shared prefixes share nodes.
Focus on recognizing:
“Prefix” / “autocomplete” / “dictionary of words” → trie
Pattern 1: Insert, Search, StartsWith
Watch cat, car and dog build a trie — shared prefix ca, then queries walk or fail. Press ▶ to animate.
⚠️ Animation & Content Notice
The animation work is not fully finished — some animations may have slight errors.
If there is a major error in the content or if the animation or content is difficult to understand, please contact us at rayyancodingschool@gmail.com.
Trie — Insert, Search & Prefix
Build a trie from a small word list and use it to test exact membership and prefixes.
Each word is inserted by walking or creating a child per character, so shared prefixes reuse nodes. Search walks the characters and requires the end-mark; startsWith succeeds as soon as the prefix path exists.
1
insert(w): cur = root
2
for ch in w:
3
if ch not in cur.children: create node
4
cur = cur.children[ch]
5
cur.isEnd = true
One node class, three methods:
class Trie {
private final Trie[] children = new Trie[26];
private boolean isEnd;
public void insert(String word) {
Trie cur = this;
for (char c : word.toCharArray()) {
int i = c - 'a';
if (cur.children[i] == null)
cur.children[i] = new Trie();
cur = cur.children[i];
}
cur.isEnd = true;
}
private Trie walk(String s) {
Trie cur = this;
for (char c : s.toCharArray()) {
cur = cur.children[c - 'a'];
if (cur == null) return null;
}
return cur;
}
public boolean search(String word) {
Trie node = walk(word);
return node != null && node.isEnd;
}
public boolean startsWith(String prefix) {
return walk(prefix) != null;
}
}class TrieNode:
def __init__(self):
self.children = {}
self.is_end = False
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, word):
cur = self.root
for ch in word:
if ch not in cur.children:
cur.children[ch] = TrieNode()
cur = cur.children[ch]
cur.is_end = True
def _walk(self, s):
cur = self.root
for ch in s:
cur = cur.children.get(ch)
if cur is None:
return None
return cur
def search(self, word):
node = self._walk(word)
return node is not None and node.is_end
def starts_with(self, prefix):
return self._walk(prefix) is not Nonestruct TrieNode {
TrieNode* children[26] = {};
bool isEnd = false;
};
class Trie {
TrieNode root;
TrieNode* walk(const string& s) {
TrieNode* cur = &root;
for (char c : s) {
cur = cur->children[c - 'a'];
if (!cur) return nullptr;
}
return cur;
}
public:
void insert(const string& word) {
TrieNode* cur = &root;
for (char c : word) {
auto& next = cur->children[c - 'a'];
if (!next) next = new TrieNode();
cur = next;
}
cur->isEnd = true;
}
bool search(const string& word) {
TrieNode* n = walk(word);
return n && n->isEnd;
}
bool startsWith(const string& prefix) {
return walk(prefix) != nullptr;
}
};class TrieNode {
children = new Map();
isEnd = false;
}
class Trie {
root = new TrieNode();
insert(word) {
let cur = this.root;
for (const ch of word) {
if (!cur.children.has(ch))
cur.children.set(ch, new TrieNode());
cur = cur.children.get(ch);
}
cur.isEnd = true;
}
#walk(s) {
let cur = this.root;
for (const ch of s) {
cur = cur.children.get(ch);
if (!cur) return null;
}
return cur;
}
search(word) {
const node = this.#walk(word);
return node !== null && node.isEnd;
}
startsWith(prefix) {
return this.#walk(prefix) !== null;
}
}
searchneedsisEnd—"car"exists inside"card"but isn’t a stored word.
Insert creates missing nodes; query just walks.
isEndseparates words from prefixes.
Pattern 2: Autocomplete (All Words With Prefix)
Walk the prefix once, then harvest every end-marked word below.
⚠️ Animation & Content Notice
The animation work is not fully finished — some animations may have slight errors.
If there is a major error in the content or if the animation or content is difficult to understand, please contact us at rayyancodingschool@gmail.com.
Trie Autocomplete
Given a prefix, return every word in the trie that begins with it.
Walk the prefix path; if it exists, DFS the subtree below it and emit every node marked as a word end.
1
node = walk(prefix) // 'ca'
2
if node == null → no matches
3
DFS from node:
4
at isEnd → emit word
DFS from the prefix’s end node:
public List<String> autocomplete(String prefix) {
List<String> result = new ArrayList<>();
TrieNode node = walkTo(root, prefix);
if (node != null)
dfs(node, new StringBuilder(prefix), result);
return result;
}
private void dfs(TrieNode node, StringBuilder path,
List<String> out) {
if (node.isEnd) out.add(path.toString());
for (int i = 0; i < 26; i++) {
if (node.children[i] != null) {
path.append((char) ('a' + i));
dfs(node.children[i], path, out);
path.deleteCharAt(path.length() - 1);
}
}
}def autocomplete(root, prefix):
# walk to prefix end
node = root
for ch in prefix:
node = node.children.get(ch)
if node is None:
return []
result = []
def dfs(cur, path):
if cur.is_end:
result.append("".join(path))
for ch, child in cur.children.items():
path.append(ch)
dfs(child, path)
path.pop()
dfs(node, list(prefix))
return resultvoid dfs(TrieNode* node, string& path, vector<string>& out) {
if (node->isEnd) out.push_back(path);
for (int i = 0; i < 26; i++) {
if (node->children[i]) {
path.push_back('a' + i);
dfs(node->children[i], path, out);
path.pop_back();
}
}
}
vector<string> autocomplete(TrieNode* root, const string& prefix) {
TrieNode* node = root;
for (char c : prefix) {
node = node->children[c - 'a'];
if (!node) return {};
}
vector<string> out;
string path = prefix;
dfs(node, path, out);
return out;
}function autocomplete(root, prefix) {
let node = root;
for (const ch of prefix) {
node = node.children.get(ch);
if (!node) return [];
}
const out = [];
const dfs = (cur, path) => {
if (cur.isEnd) out.push(path);
for (const [ch, child] of cur.children)
dfs(child, path + ch);
};
dfs(node, prefix);
return out;
}The prefix itself is the DFS starting path — everything below it shares that stem.
Common Mistakes
Forgetting isEnd in search.
startsWith("ca") succeeds even though "ca" was never inserted as a word.
Rebuilding the trie per query.
Build once at construction; queries are pure walks.
Using a HashMap when alphabet is fixed a–z.
Array-of-26 is faster and idiomatic in Java/C++; maps shine with unicode.
Complexity
| Operation | Time | Space |
|---|---|---|
| insert / search / startsWith | O(m) — word length | O(total chars) |
| Autocomplete(prefix) | O(P + matches·L) | O(depth) recursion |
Premium Content
Unlock Basic Trie and all premium lessons with a subscription.
From ₹199.99/year — See plans