Back to DSA Guide Catalog

Data Structurehard

Trie (Prefix Tree) Complete Guide

Trie is a prefix tree where each edge represents a character and shared prefixes are stored once.

It is optimized for prefix queries, autocomplete, dictionary checks, and lexicographic traversal tasks.

Trie trades extra memory for fast prefix operations.

Typical Complexity Baseline

MetricValue
Insert/search prefixO(length of word)

Video Explainer

Prefer video learning? This explainer gives a quick visual walkthrough of the core idea before you dive into the detailed sections below.

Core Concepts

Learn the core building blocks and terminology in one place before comparisons, so the mechanics are clear and duplicates are removed.

Root node

What it is: Entry node with no character.

Why it matters: Root node is a required building block for understanding how Trie (Prefix Tree) stays correct and performant on large inputs.

Children map

What it is: Character -> next node mapping.

Why it matters: Children map is a required building block for understanding how Trie (Prefix Tree) stays correct and performant on large inputs.

End marker

What it is: Flag marking completed word.

Why it matters: End marker is a required building block for understanding how Trie (Prefix Tree) stays correct and performant on large inputs.

Prefix path

What it is: Node path representing query prefix.

Why it matters: Prefix path is a required building block for understanding how Trie (Prefix Tree) stays correct and performant on large inputs.

Optional payload

What it is: Metadata stored at terminal nodes.

Why it matters: Optional payload is a required building block for understanding how Trie (Prefix Tree) stays correct and performant on large inputs.

Prefix

What it is: Starting substring of a word.

Why it matters: Starting substring of a word. In Trie (Prefix Tree), this definition helps you reason about correctness and complexity when inputs scale.

Terminal node

What it is: Node representing end of a valid word.

Why it matters: Node representing end of a valid word. In Trie (Prefix Tree), this definition helps you reason about correctness and complexity when inputs scale.

Compressed trie

What it is: Trie variant collapsing single-child paths.

Why it matters: Trie variant collapsing single-child paths. In Trie (Prefix Tree), this definition helps you reason about correctness and complexity when inputs scale.

Radix tree

What it is: Space-optimized trie with merged edges.

Why it matters: Space-optimized trie with merged edges. In Trie (Prefix Tree), this definition helps you reason about correctness and complexity when inputs scale.

Branching factor

What it is: Average number of children per node.

Why it matters: Average number of children per node. In Trie (Prefix Tree), this definition helps you reason about correctness and complexity when inputs scale.

Putting It All Together

This walkthrough connects the core concepts of Trie (Prefix Tree) into one end-to-end execution flow.

Step 1

Root node

Entry node with no character.

Before

  • Insert word: "cat"
  • Root has no children

After

  • Trie stores one word path
  • Prefix 'ca' now exists

Transition

Create nodes c -> a -> t
Mark terminal at t

Why this step matters: Root node is a required building block for understanding how Trie (Prefix Tree) stays correct and performant on large inputs.

Step 2

Children map

Character -> next node mapping.

Before

  • Insert word: "car"
  • Prefix c -> a already present

After

  • Shared memory for prefix
  • Words: cat, car

Transition

Reuse shared prefix nodes
Create new branch node r

Why this step matters: Children map is a required building block for understanding how Trie (Prefix Tree) stays correct and performant on large inputs.

Step 3

End marker

Flag marking completed word.

Before

  • Query prefix: "ca"
  • Traversal starts at root

After

  • Prefix exists = true
  • Autocomplete candidates available

Transition

Follow c then a child edges
Stop after prefix consumed

Why this step matters: End marker is a required building block for understanding how Trie (Prefix Tree) stays correct and performant on large inputs.

Step 4

Prefix path

Node path representing query prefix.

Before

  • Query full word: "cap"
  • Prefix path partially exists

After

  • Word exists = false
  • Lookup short-circuits early

Transition

Traverse c->a
Missing child p

Why this step matters: Prefix path is a required building block for understanding how Trie (Prefix Tree) stays correct and performant on large inputs.

How It Compares

vs Hash map of full strings

When to choose this: Choose trie for frequent prefix queries.

Tradeoff: Hash map is simpler and often lighter for exact-match only.

vs Sorted array + binary search

When to choose this: Choose trie when many prefix checks are needed online.

Tradeoff: Sorted arrays can be more memory-efficient for static dictionaries.

vs Suffix array/tree

When to choose this: Choose trie for prefix-oriented lookups.

Tradeoff: Suffix structures target substring search across whole text.

Real-World Stories and Company Examples

Google

Autocomplete-style systems rely on prefix indexing strategies closely related to trie behavior.

Takeaway: Prefix-first retrieval is the core value proposition.

VS Code ecosystem

Code completion engines maintain prefix-indexed symbol data for fast suggestion filtering.

Takeaway: Trie-like indexing improves developer UX latency.

Cloudflare

Domain and rule matching systems benefit from prefix tree structures for hierarchical matching.

Takeaway: Trie structures map naturally to hierarchical token spaces.

Implementation Guide

Implement nodes and child slots directly so prefix-tree behavior is fully explicit without built-in hash maps.

Complexity: Time O(L) per insert/search, L = word length

Trie insert + search

type TrieNode = { isWord: boolean; children: Array<TrieNode | null> }

function createTrieNode(): TrieNode {
  return { isWord: false, children: new Array<TrieNode | null>(26).fill(null) }
}

function charIndex(char: string): number {
  return char.charCodeAt(0) - 97
}

function insertWord(rootNode: TrieNode, word: string): void {
  let currentNode = rootNode
  for (let index = 0; index < word.length; index += 1) {
    const childIndex = charIndex(word[index])
    if (!currentNode.children[childIndex]) {
      currentNode.children[childIndex] = createTrieNode()
    }
    currentNode = currentNode.children[childIndex]!
  }
  currentNode.isWord = true
}

function searchWord(rootNode: TrieNode, word: string): boolean {
  let currentNode: TrieNode | null = rootNode
  for (let index = 0; index < word.length && currentNode; index += 1) {
    currentNode = currentNode.children[charIndex(word[index])]
  }
  return Boolean(currentNode?.isWord)
}

Common Problems and Failure Modes

  • Underestimating memory usage for large alphabets.
  • Not cleaning unused nodes during deletion.
  • Treating prefix match as full-word match without terminal checks.
  • Choosing trie for tiny datasets where map is simpler.
  • Using dense arrays for sparse alphabets and wasting space.

Tips and Tricks

  • Trie is ideal when prefix lookups dominate workload.
  • Store end-of-word flags explicitly; prefix existence is not word existence.
  • Compress sparse branches (radix-like ideas) when memory is a concern.
  • Use trie for many repeated queries, not one-off string checks.

When to Use

Use these signals to decide if this data structure/algorithm is the right fit before implementation.

Real-system usage signals

  • Prefix lookups run in O(length of query).
  • Shared prefixes avoid repeated string comparisons.
  • Useful for autocomplete and dictionary-style search features.

LeetCode-specific tips (including pattern-identification signals)

  • Identification signal: many prefix queries over word dictionary (autocomplete, startsWith, wildcard prefix checks).
  • Identification signal: repeated prefix lookups are too slow with full string scans each time.
  • If the prompt combines insert/search prefix operations at scale, trie is usually stronger than plain hash-map.
  • For Trie (Prefix Tree) questions, start by naming the core invariant before writing code.
  • Use the constraint section to set time/space target first, then pick the data structure/algorithm.
  • Solve one tiny example by hand and map each transition to your variables before implementing.
  • Run adversarial cases: empty input, duplicates, max-size input, and sorted/reverse patterns when relevant.
  • During interviews, explain why your approach is the right pattern for this prompt, not just why the code works.

LeetCode Progression (Easy to Hard)

Back to DSA Guide Catalog
#ProblemDifficultyTypical Complexity
1Longest Common PrefixEasyO(n*m)
2Implement Trie (Prefix Tree)MediumO(L)
3Design Add and Search Words Data StructureMediumO(L) avg
4Replace WordsMediumO(total chars)
5Map Sum PairsMediumO(L)
6Maximum XOR of Two Numbers in an ArrayMediumO(n * bits)
7Word Search IIHardPruned DFS
8Palindrome PairsHardO(n*k^2)
9Stream of CharactersHardO(L) per query
10Word SquaresHardBacktracking + trie