🎉 All 30 days are live — the full DSA-30 course, from Big-O to System Design. See the roadmap →
Day 18 - TriesCore Operations

Trie — Core Operations

Three operations, one shared shape: walk the path character by character. insert creates nodes as it goes; search and startsWith just follow existing nodes and report whether the walk succeeds. Once you’ve written one, the others are variations.

Try it first

Insert a few words, then search for an exact word and startsWith a prefix. Watch how search and startsWith differ on a prefix that isn’t itself a word (insert card, then search("car") vs startsWith("car")).

Try it — insert words, then search or check a prefix
Words in the trie: cat, car, card, dog, do
cardtdog

The node

The one design decision is how to store children. Two options:

  • Array of size 26 (children[26]) — for lowercase a–z. O(1) indexing, but every node reserves 26 slots even if it uses one. Fast, memory-heavy.
  • Hash map (char → node) — allocates only the characters actually used. Slightly slower per step, far less memory for sparse/large alphabets (Unicode, mixed case).

Use the array for a–z interview problems (it’s faster and the alphabet is tiny); use the map when the alphabet is large or sparse.

insert, search, startsWith

Notice all three operations funnel through one walk helper. search adds an isEnd check; startsWith doesn’t. insert is walk that creates missing nodes instead of bailing. That symmetry is worth pointing out in an interview — it shows you see the shared structure.

Collecting all words under a prefix (autocomplete)

The prefix-query superpower: walk to the prefix node, then DFS the subtree, emitting a word at every isEnd.

This is the engine behind autocomplete. The cost is O(L) to reach the prefix plus O(size of the subtree) to enumerate — you only ever touch words that actually share the prefix.

Deletion (the tricky one)

Deleting is rarely asked but worth knowing. You can’t just unmark isEnd and walk away if you care about memory — you should prune nodes that become useless. The rule:

Walk to the word’s end node; unset isEnd

If the path doesn’t fully exist, the word isn’t there — nothing to do.

Prune bottom-up

Going back up, delete a node only if it has no children and is not itself an end-of-word for some other word. Stop pruning the moment you hit a node that’s still needed (it has another child, or marks a shorter word).

⚠️

The deletion trap: never prune a node another word still needs. If you delete do from a trie that also holds dog, you must only clear the is_end flag on the o node — you cannot remove the d→o path, because dog walks through it. Pruning is allowed solely for nodes that become leaves with no end-flag. Get this wrong and you silently delete unrelated words.

Complexity recap

OperationTimeNotes
insert, search, startsWithO(L)L = word length, independent of word count
autocomplete (prefix → all words)O(L + subtree)only touches matching words
deleteO(L)plus pruning along the path

Quick check

In the code above, what's the ONLY difference between search(word) and startsWith(word)?
You implement children as an array of 26 pointers per node. What's the main downside versus a hash map?
Why must trie deletion prune nodes bottom-up and stop at the first 'still-needed' node?

Next: Variations & Tricks — compressed/radix tries, the bitwise trie that cracks XOR problems, and storing values at nodes.