CMD Guide
HomeDSATrie

Introduction to Trie

A Trie stores a set of strings by sharing common prefixes as a single walked path through a tree, so that inserting, searching, or checking a prefix costs time proportional only to the length of the string being processed, never to how many other strings are stored.

Recognize the pattern

Brute force to optimal

Brute force: keep all words in an array/hash set. Exact search is O(1) average (hash) or O(n) (array), but a prefix query — "find all words starting with 'ca'" — requires scanning every one of the n words and checking startsWith, costing O(n·L) where L is average word length. Autocomplete-as-you-type triggers this scan on every keystroke.

Optimal (Trie): walk one edge per character from the root. Insert, search, and prefix-check all cost O(L) — independent of how many words n are stored. The trade-off is memory: each node reserves space for every possible next character (e.g. 26 pointers), so a sparse alphabet wastes space unless you use a hashmap per node instead of a fixed array.

Complexity, derived

Let L = length of the word being inserted/searched, Σ = alphabet size (26 for lowercase English), N = total characters across all stored words.

Worked example: insert {"car", "cat", "cart"}, then search "cat" and prefix "ca"

StepActionTrie state (path shown)
1insert "car"root→c→a→r (r.isEnd=true)
2insert "cat"reuse root→c→a, create new t (t.isEnd=true)
3insert "cart"reuse root→c→a→r, create new t (t.isEnd=true)
4search "cat"walk c→a→t, 3 hops, t.isEnd=true → found
5startsWith "ca"walk c→a, 2 hops, node exists → true (regardless of isEnd)
6search "ca"walk c→a, node exists but isEnd=false → false ("ca" is a stored prefix, not a stored word)

Only 5 nodes were created (plus the pre-existing root, 6 nodes total) for 10 characters of input — the shared "ca" prefix was stored once.

Java implementation

class TrieNode {
    TrieNode[] children = new TrieNode[26];
    boolean isEnd = false;
}

class Trie {
    private final TrieNode root = new TrieNode();

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

    public boolean search(String word) {
        TrieNode node = walk(word);
        return node != null && node.isEnd;
    }

    public boolean startsWith(String prefix) {
        return walk(prefix) != null;
    }

    private TrieNode walk(String s) {
        TrieNode node = root;
        for (char c : s.toCharArray()) {
            int i = c - 'a';
            if (node.children[i] == null) return null;
            node = node.children[i];
        }
        return node;
    }
}

Trie Memory Optimization: Radix Trees & Ternary Search Trees

While standard Tries offer optimal time complexity, they are notoriously memory-inefficient. In a 64-bit JVM, each standard TrieNode object has object header overhead (16 bytes) plus a 26-pointer array references (104 bytes), meaning a naive Trie node uses at least 120 bytes of heap space just to store a single letter. At scale, this causes significant memory pressure. Production systems use two primary optimizations to mitigate this:

  1. Radix Tree (Compressed Trie):
    • Mechanism: A Radix Tree compresses a Trie by merging any node that has only a single child into its parent node. The edges hold string labels rather than single characters. For example, instead of storing individual nodes for c → a → r → t, the path is collapsed into a single node with the edge label "cart".
    • Trade-off: It drastically reduces the number of tree nodes and pointer lookups, making it highly memory-efficient and cache-friendly for long, non-branching strings. This is the industry standard for IP routing tables (longest prefix match) and URL routers.
  2. Ternary Search Tree (TST):
    • Mechanism: A Ternary Search Tree blends the space efficiency of a Binary Search Tree (BST) with the prefix capabilities of a Trie. Each node contains a single character and exactly three pointers: left, mid, and right.
      • If the query character is smaller than the node's character, go left.
      • If it is larger, go right.
      • If it matches, go mid and advance to the next character in the query string.
    • Trade-off: Instead of reserving Σ = 26 or 256 pointers per node, a TST node uses exactly 3 pointers, reducing the space complexity to O(N) (proportional to the number of stored characters). Lookups cost O(L + log V) time due to BST searches at each level, trading a slight constant-factor speed penalty for enormous memory savings.

Pitfalls

When to use / when not

Use a Trie when you repeatedly query prefixes over a static or growing set of strings — autocomplete, spell-checkers, IP routing tables, word-search boards with dictionary pruning.

Vs. HashSet<String>: a hash set gives O(1) exact membership but cannot answer "all words starting with X" without an O(n) scan; a Trie answers it in O(L + results). If you never need prefix queries, a hash set is simpler and often more memory-efficient — don't reach for a Trie just to check membership.

Vs. sorted array + binary search: binary search gives O(L log n) prefix range lookup with far less memory overhead (no per-node pointer arrays), a reasonable choice when the dictionary is static and memory is tight.

Takeaways

Recall: Why is a Trie's search/insert cost O(L) regardless of how many total words are stored, while a naive prefix scan over an array of words costs O(n·L)?


Compiled from standard trie/prefix-tree references (CLRS-style tree traversal analysis; Sedgewick & Wayne, Algorithms, 4th ed., ternary/prefix tries chapter) and common interview-prep treatments of trie operations.

🤖 Don't fully get this? Learn it with Claude

Stuck on Introduction to Trie? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Introduction to Trie** (DSA) and want to truly understand it. Explain Introduction to Trie from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Introduction to Trie** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Introduction to Trie** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Introduction to Trie** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes