ALGORITHMS · 15  /  TRIE

The Word Tree.

Store a whole dictionary so that every shared prefix is shared just once. Insert words letter by letter, watch CAT and CAR grow the same trunk, then answer "is this a word?" or "any word start with this?" in one walk down.

THE GIST · 20 SECONDS

A trie (prefix tree) stores strings in a tree where each edge is a letter and each path from the root spells a prefix. Words that share a prefix share a path; a flag marks where a real word ends. Insert walks down creating missing nodes; search walks down and checks the end flag; startsWith just checks the path exists. Every op is O(length).

  • Nodeone letter on a path
  • Shared prefixone path, many words
  • End flaga real word stops here
  • O(L)cost = word length

Hit Step to insert the next letter — or Play and watch the words grow into a tree, then search it.

UNDER THE HOOD

What you just played, written down

A trie trades memory for speed: it factors out shared prefixes into shared paths, so a lookup costs only the length of the word — never the size of the dictionary.

How a trie thinks — four moves

  1. The root is empty. Every path starts there; each step down consumes one character of a word.
  2. Insert: walk & grow. For each letter, follow the child if it exists or create it. Mark the final node is_end = true.
  3. Share prefixes for free. CAT and CAR both walk C→A, so that prefix exists once. Only the diverging letters add nodes.
  4. Search vs startsWith. Both walk the letters. search also requires the end flag; startsWith only needs the path to exist.
WHY THE END FLAG MATTERS

Inserting CAR and CARD means the node after CAR is a real word and a prefix of a longer one. The is_end flag is how the trie tells "this is a stored word" apart from "this is only a prefix so far."

The whole thing in code

class Trie:
    def __init__(self):
        self.kids = {}          # letter -> Trie node
        self.end = False

    def insert(self, word):
        node = self
        for ch in word:
            if ch not in node.kids:
                node.kids[ch] = Trie()   # grow
            node = node.kids[ch]
        node.end = True          # a word ends here

    def search(self, word):
        node = self._walk(word)
        return node is not None and node.end

    def starts_with(self, prefix):
        return self._walk(prefix) is not None

    def _walk(self, s):
        node = self
        for ch in s:
            if ch not in node.kids: return None
            node = node.kids[ch]
        return node
TIMEO(L)per insert / search
SPACEO(Σ·N)chars × alphabet
WHY NOT JUST A HASH SET?

A hash set answers "is this exact word here?" fast — but it can't answer "which words start with 'car'?" without scanning everything. The trie makes prefix queries and autocomplete natural and cheap.

⚠ Memory can balloon

A node per character per branch is a lot of pointers. Compressed tries (radix trees) merge single-child chains, and array-vs-map children trade speed for space depending on the alphabet.

↔ Its relatives

Radix / Patricia tries compress chains; suffix tries/trees index every suffix of one string; Aho-Corasick adds KMP-style failure links for multi-pattern search.

★ Where it's used

Autocomplete & type-ahead, spell-checkers, IP routing (longest-prefix match), T9 predictive text, and dictionary compression.

SOLVE IT YOURSELF

Solve it: build a Trie

This one is yours to write — in Python or TypeScript, running for real in your browser. Implement insert, search, and startsWith. Edit the stub, hit Run (or ⌘/Ctrl + Enter), and watch the hidden tests. Stuck? the hints are below and Reveal solution is one click away.

YOUR TASK

Implement a Trie class with insert(word), search(word) (exact word present?), and starts_with(prefix) (any word begins with this prefix?). Each node maps a character to a child node and carries an end-of-word flag.

HINTS — 4 IDEAS
  1. Represent each node as a dict of char → node plus a boolean end.
  2. insert: walk the word, creating missing children, then mark the last node end = True.
  3. search: walk the word; return True only if you finish and the final node's end is set.
  4. starts_with: walk the prefix; return True if the whole path exists (ignore the end flag).
CPython · WebAssembly
QUICK CHECK

Did it stick?

FAQ

Tries, answered

What is a trie?

A prefix tree: a tree where each edge is a character and each path from the root spells a prefix. Words that share a prefix share a path, and an end-of-word flag marks where a real word stops. It's the classic structure behind autocomplete.

How does searching work?

Start at the root and follow the child edge for each character. If a needed child is missing, the word isn't there. If you consume all characters and the final node's end flag is set, the word is stored.

search vs startsWith?

Both walk the characters. search also requires the final node's end flag (the exact word was inserted); startsWith only needs the path to exist (some word begins with that prefix).

What's the complexity?

Insert, search, and startsWith are all O(L) — the length of the word or prefix — independent of how many words are stored. Space is O(total characters × alphabet), which can be large.

Where are tries used?

Autocomplete and type-ahead, spell-checkers, IP routing (longest-prefix match), T9 predictive text, and multi-pattern search via Aho-Corasick.

RESULT

Next → hang KMP-style failure links off every trie node and you can match many patterns in one text pass — that's the Aho-Corasick automaton.

Finished this one? 0 / 17 Algorithms done

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More Algorithms