Tries · Trie Fundamentals

Trie Implementation

Storing a set of strings as paths through a 26-way branching tree, one character per edge, so that insert/search/prefix-check all become simple root-to-node walks - shared prefixes across words automatically share the same path, giving each operation O(word length) cost regardless of how many words are stored.

  • 5 min read
  • 7 practice questions
  • 2 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: wildcard search
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

Autocomplete must list every word starting with what the user has typed, from a dictionary of a million words, on every keystroke. A hash set answers "is this exact word there?" but has no idea which words start with "inte". A sorted list can binary-search for the range. A trie organizes the words by their prefixes directly.

This skill builds a trie: words as paths of characters, shared prefixes sharing nodes, and an end mark that separates "a word ends here" from "some word passes through". It also covers wildcard search, which branches at each "." and must still finish on an end mark.

We'll go from simple to more technical:

  1. The pattern: insert, search and startsWith over a node per character.
  2. A second flavor: wildcard search that tries every child at a ".".
  3. Where tries run (autocomplete, spell checkers, IP routing, search-engine dictionaries).
  4. What breaks it: no end-mark check, index math on other characters, wildcards, memory.

The approach

Java

class TrieNode {
    TrieNode[] next = new TrieNode[26];
    boolean isEnd;                                   // a complete word ends exactly here
}

void insert(String word) {
    TrieNode node = root;
    for (char c : word.toCharArray()) {
        if (node.next[c - 'a'] == null) node.next[c - 'a'] = new TrieNode();
        node = node.next[c - 'a'];
    }
    node.isEnd = true;
}

TrieNode find(String s) {                            // the node where s's path ends, or null
    TrieNode node = root;
    for (char c : s.toCharArray()) {
        node = node.next[c - 'a'];
        if (node == null) return null;
    }
    return node;
}

boolean search(String word)       { TrieNode n = find(word); return n != null && n.isEnd; }
boolean startsWith(String prefix) { return find(prefix) != null; }

Python

class TrieNode:
    def __init__(self):
        self.next = {}                               # letter -> TrieNode: any alphabet
        self.is_end = False                          # a complete word ends exactly here


def insert(word: str) -> None:
    node = root
    for c in word:
        if c not in node.next:
            node.next[c] = TrieNode()
        node = node.next[c]
    node.is_end = True


def find(s: str) -> TrieNode | None:                 # the node where s's path ends, or None
    node = root
    for c in s:
        node = node.next.get(c)
        if node is None:
            return None
    return node


def search(word: str) -> bool:
    node = find(word)
    return node is not None and node.is_end


def starts_with(prefix: str) -> bool:
    return find(prefix) is not None

Every word is a path; words with a common prefix share its nodes. Each operation walks at most L nodes - O(L), independent of the dictionary's size. The end mark is what separates the two questions: after inserting "carpet", startsWith("car") is true but search("car") is false - the path exists, but no word ends there. Returning find(word) != null from search is the classic bug.

"Does any stored word match b.d?" - where . stands for any letter - is a walk that branches at each wildcard:

Java

boolean match(String word, int i, TrieNode node) {
    if (node == null) return false;
    if (i == word.length()) return node.isEnd;       // must end exactly at a word
    char c = word.charAt(i);
    if (c != '.') return match(word, i + 1, node.next[c - 'a']);
    for (TrieNode child : node.next) {               // '.' tries every existing child
        if (child != null && match(word, i + 1, child)) return true;
    }
    return false;
}

Python

def match(word: str, i: int, node: TrieNode | None) -> bool:
    if node is None:
        return False
    if i == len(word):
        return node.is_end                           # must end exactly at a word
    c = word[i]
    if c != ".":
        return match(word, i + 1, node.next.get(c))
    return any(match(word, i + 1, child) for child in node.next.values())   # '.' tries every existing child

Letters follow one child; a . tries each child that exists. The search is bounded by the trie's size, but a pattern of all dots can visit every node of that length.

Where you'll see it

Search-box autocomplete, spell checkers, T9-style keypad lookup, and IP routers (a binary trie over address bits finds the longest matching network prefix) are tries. Search engines often store their term dictionaries as compressed tries or finite-state transducers (Lucene's FST) to answer prefix and fuzzy queries.

Which tool for which job?

You needUse
Exact lookups onlya hash set
Prefix queries on a set that changesa trie
Prefix queries on a fixed lista sorted list and two binary searches
Long keys that rarely brancha radix (compressed) trie
Patterns with single-letter wildcardsa trie walk that branches at each "."
Characters beyond a-zmap children, not a 26-slot array

Common mistakes

MistakeWhat happensDo this instead
search returns "the path exists"every prefix of a word counts as a wordcheck the end mark
c - 'a' on other characters"Cafe" indexes -30 and throwslowercase first, or map children
Wildcards that stop at any node"ab." matches "abcd"require the end mark when the pattern runs out
A 26-slot array per node for sparse datamostly empty memorymap children, or a radix trie
Leading wildcards on a big trie"....z" visited 18,688 nodesput fixed letters first when you can

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. After inserting only "carpet", what do search("car") and startsWith("car") return, and why?

Show answer

false and true. The path c-a-r exists, so a word starts with "car", but the node after the r isn't marked as the end of a word.

2. Why does a trie operation cost O(L) regardless of how many words are stored?

Show answer

It walks one node per character of the query, following a direct child pointer each time; other words only add branches it never visits.

3. In wildcard search, what must be true when the pattern runs out?

Show answer

The current node must be marked as the end of a word. Reaching a node only means some stored word has this pattern as a prefix; "ab." would otherwise match a stored "abcd".

Key takeaways

  • Words are paths; shared prefixes share nodes; operations are O(L).
  • search needs the end mark; startsWith only needs the path.
  • Wildcards branch over existing children and still need the end mark.
  • Arrays for small alphabets, maps for large ones; radix tries compress long chains.

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
Exact lookupshash set
Prefixes, changing settrie
Prefixes, fixed listsorted list + bisect
Long, rarely branching keysradix trie
Wildcardsbranch at "."
Any charactersmap children

Snippets worth knowing by heart

Java

boolean search(String w)     { TrieNode n = find(w); return n != null && n.isEnd; }   // end mark!
boolean startsWith(String p) { return find(p) != null; }

Python

import bisect
lo = bisect.bisect_left(words, prefix)                     # words is sorted
hi = bisect.bisect_left(words, prefix + chr(0x10FFFF))
matches = words[lo:hi]

The traps

  • No end-mark check: every prefix of a word counts as a word. Check the end mark in search.
  • c - 'a' on other characters: "Cafe" indexes -30 and throws. Lowercase first, or use map children.
  • Wildcards without the end mark: "ab." matches "abcd". Require the end mark when the pattern runs out.
  • 26-slot arrays for sparse data: most memory is empty slots. Use map children, or a radix trie.

Staff-level points, one line each

  • 10,000 user:...:profile keys: 126,426 trie nodes, 14,044 radix-trie nodes.
  • CPython 3.14: a 26-slot list is 264 bytes, a two-child dict 184.
  • A sorted list and two binary searches matched the trie on 300 prefix queries.
  • Wildcards: "a...." visited 1,045 nodes, "....z" 18,688, "....." all 28,279.
  • c - 'a' threw for "Cafe" (index -30) and "café" (index 136).

Say it in an interview

  • "Each word is a path, and shared prefixes share nodes, so operations are O(L)."
  • "search needs the end mark; startsWith only needs the path."
  • "For a static list I could binary-search a sorted array instead."
  • "Long keys call for a radix trie."

Practice

This skill comes with 7 multiple-choice questions that tell you which misconception a wrong answer reveals and 2 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Trie Implementation free

Trie Implementation (Insert, Search, StartsWith)

MediumProCoding problemJava · Python

Implement a trie with insert(word), search(word) (returns whether the exact word was inserted), and startsWith(prefix) (returns whether any inserted word starts with this prefix).

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Add and Search Words

MediumProCoding problemJava · Python

Build a word dictionary with two operations: addWord(word) stores a word, and search(pattern) tells you whether some stored word matches the pattern. In a pattern, . matches any one letter, and a match must be the same length as the word.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Trie Implementation free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime