208. Implement Trie (Prefix Tree)
mediumAsked at LinkedInImplement a Trie with insert, search, and startsWith. LinkedIn asks this because it's the foundation of their typeahead and autocomplete features — they want a clean node-with-children-map structure and the isEnd boolean.
By Alex Chen, Founder, InterviewChamp.AI · Last verified
Source citations
Public interview reports confirming this problem appears in LinkedIn loops.
- Glassdoor (2026-Q1)— LinkedIn SWE onsite reports list Trie implementation as a 30-minute warm-up before an autocomplete or word-search follow-up.
- Blind (2025-12)— LinkedIn writeups specifically tie this to their typeahead/autocomplete use cases as the why-this-question signal.
Problem
A trie (pronounced as 'try') or prefix tree is a tree data structure used to efficiently store and retrieve keys in a dataset of strings. There are various applications of this data structure, such as autocomplete and spellchecker. Implement the Trie class: Trie() Initializes the trie object. void insert(String word) Inserts the string word into the trie. boolean search(String word) Returns true if the string word is in the trie (i.e., was inserted before), and false otherwise. boolean startsWith(String prefix) Returns true if there is a previously inserted string word that has the prefix prefix, and false otherwise.
Constraints
1 <= word.length, prefix.length <= 2000word and prefix consist only of lowercase English letters.At most 3 * 10^4 calls in total will be made to insert, search, and startsWith.
Examples
Example 1
["Trie", "insert", "search", "search", "startsWith", "insert", "search"] [[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]][null, null, true, false, true, null, true]Approaches
1. Hash map of children + isEnd boolean (canonical)
Each node has a Map of char to child node and a boolean isEnd. Insert walks/creates nodes per char and sets isEnd at the leaf. Search walks; returns true only if reached and isEnd. startsWith walks; returns true if reached (no isEnd check).
- Time
- O(L) per operation where L is word length
- Space
- O(total characters across all inserts)
class Trie {
constructor() { this.root = { children: new Map(), isEnd: false }; }
insert(word) {
let node = this.root;
for (const c of word) {
if (!node.children.has(c)) node.children.set(c, { children: new Map(), isEnd: false });
node = node.children.get(c);
}
node.isEnd = true;
}
search(word) {
const node = this._walk(word);
return node !== null && node.isEnd;
}
startsWith(prefix) {
return this._walk(prefix) !== null;
}
_walk(str) {
let node = this.root;
for (const c of str) {
if (!node.children.has(c)) return null;
node = node.children.get(c);
}
return node;
}
}Tradeoff: Map-based — handles arbitrary character sets. The shared _walk helper avoids duplicating the traversal logic between search and startsWith.
2. Array-of-26 children (lowercase optimization)
Replace the Map with a 26-slot array indexed by char - 'a'. Faster constant-factor but only works for lowercase a-z.
- Time
- O(L)
- Space
- O(26 * nodes)
class TrieArray {
constructor() { this.root = { children: new Array(26).fill(null), isEnd: false }; }
insert(word) {
let node = this.root;
for (const c of word) {
const i = c.charCodeAt(0) - 97;
if (!node.children[i]) node.children[i] = { children: new Array(26).fill(null), isEnd: false };
node = node.children[i];
}
node.isEnd = true;
}
search(word) {
const node = this._walk(word);
return node !== null && node.isEnd;
}
startsWith(prefix) { return this._walk(prefix) !== null; }
_walk(str) {
let node = this.root;
for (const c of str) {
const i = c.charCodeAt(0) - 97;
if (!node.children[i]) return null;
node = node.children[i];
}
return node;
}
}Tradeoff: Faster array access vs Map hash. Uses 26 * 8 = 208 bytes per node (sparse). Worth it for high-throughput typeahead workloads; otherwise Map is cleaner.
LinkedIn-specific tips
LinkedIn interviewers grade two specific things: (1) Do you separate search vs startsWith correctly? Search must also check isEnd; startsWith does not. (2) Do you spot the shared _walk helper? It's a 30-second refactor that signals you write maintainable code. Be ready for the follow-up: 'How would you delete a word?' (Recurse to the leaf, unset isEnd, prune empty subtrees on the way up.)
Common mistakes
- Treating search and startsWith identically — search must require isEnd === true.
- Initializing isEnd globally in the class instead of per-node — every node would share the same flag.
- In the array version, forgetting to initialize children as Array(26).fill(null) — gives undefined behavior on unfilled slots.
Follow-up questions
An interviewer at LinkedIn may pivot to one of these next:
- Add a delete(word) method.
- Design Add and Search Words Data Structure (LC 211) — wildcard '.' support.
- Word Search II (LC 212) — use a Trie to search multiple words in a grid simultaneously.
Solve it now
Free. No sign-up. Python and JavaScript run instantly in your browser.
FAQ
When should you use a Map vs an array for children?
Array (size 26 or 256) for fixed alphabets — faster constant factor. Map for arbitrary or Unicode chars — flexible but slower per-access. For LinkedIn's typeahead workload, the array version typically wins.
Why is isEnd needed?
Because 'app' and 'apple' share a prefix; without isEnd you can't distinguish 'app was inserted' from 'app is just a prefix of apple.' isEnd marks where actual words end.
Free learning resources
Curated free links for this problem.
Practice these live with InterviewChamp.AI
Drill Implement Trie (Prefix Tree) and other LinkedIn interview questions under real-loop conditions with instant feedback on your reasoning, complexity claims, and code.
Practice these live with InterviewChamp.AI →