AlgoMaster Logo

Introduction to Trie

High Priority10 min readUpdated June 4, 2026
Listen to this chapter
Unlock Audio

Tries, also known as Prefix Trees, are a tree data structure that stores and searches strings efficiently by reusing shared prefixes.

Tries power familiar features like search-engine query suggestions and spell checkers. They are also a common topic in coding interviews, especially in problems that involve prefix-based search or efficient string lookups.

This chapter covers:

  • What a trie is and how it works
  • The structure of a TrieNode
  • Common trie operations and their time complexities

What is a Trie?

A trie is a tree-based data structure that stores strings so prefix lookups run in time proportional to the prefix length.

Instead of storing whole words as standalone entries, a trie breaks them down character by character and reuses common prefixes.

Start with the word "design". In a trie, we store it as a sequence of connected nodes, where each node represents a single character.

rootdesign

Following the path from the root to 'n' gives the word "design".

Now add another word: "desktop".

rootdesikgtnop

"design" and "desktop" both start with "des". Instead of creating a completely new branch, the trie reuses the prefix "des".

Next, insert the word "destination".

It also starts with "des", so we share that prefix again and branch after it:

rootdesiktgtinonpation

To summarize:

  • Each node represents a character of the string.
  • A path from the root to a leaf (or marked node) represents a word.
  • Multiple words that share the same prefix will reuse the same starting path in the trie.

In code, a trie is built from a small node type, the TrieNode.

The structure of a TrieNode

To represent a trie in code, we first need to define the building block of the trie, the TrieNode.

Each node in a trie keeps track of two important things:

  1. Children
    • Each node can branch out to multiple children, one for every possible character that comes next.
    • In code, this is often represented using a hash map where the key is the character and the value is the child node.
    • But if the character set is fixed and small like 26 lowercase English letters, we can use an array of size 26 instead. This makes lookups faster because each index directly maps to a letter.
  2. End of Word Marker
    • Not every node represents a complete word. Some are just prefixes.
    • To distinguish between a prefix and an actual word, we mark the nodes where a word ends.
    • In code, this is usually done with a boolean flag like isWord.

Going back to our earlier example with the words "design", "desktop", and "destination":

  • The characters d → e → s are shared among all three words.
  • After "des", the trie branches into different paths depending on whether we're building "design", "desktop", or "destination".
  • At the end of each word's path, the final node's isWord flag is set to true.

All major trie operations like insert, search, and prefix search only require us to walk through the characters of the word once. So the time complexity is O(k) where k is the length of the word.

Prefix-heavy problems benefit directly from this property: prefix queries cost O(k), where k is the prefix length.

Trie Operations

Four operations cover almost every trie problem: insert, search, prefix search, and delete. The implementations below use the Java TrieNode from the previous section, where children is an array of size 26 indexed by c - 'a'.

1. Insert

Walk down the trie one character at a time. If the path does not exist yet, create the missing nodes along the way. At the end, mark the last node as a word boundary.

For the word desktop, the loop visits each character. If the trie already contains design, the nodes for d, e, and s already exist and are reused. New nodes are created for k, t, o, and p. The isWord flag on the final p node is set to true.

The time complexity is O(k) where k is the length of the word. The space complexity is O(k) in the worst case, when none of the prefix is shared with existing words.

Walk the same path the insert would have taken. If any character is missing along the way, the word is not in the trie. If the walk completes, check the isWord flag on the final node to distinguish a full word from a prefix.

The helper findNode returns the node reached by walking the prefix, or null if the path breaks. It is shared between search and startsWith.

Search runs in O(k) time and O(1) extra space.

3. StartsWith (Prefix Search)

Prefix search is the same as search, except we do not care whether the final node is a complete word. If we can walk the prefix without hitting a dead end, at least one word in the trie has that prefix.

This single operation is the reason tries exist. A hash map can answer "is this exact word stored?" but cannot answer "is any word starting with des stored?" without scanning every entry.

4. Delete

Deletion is the trickiest trie operation because we have to walk down to confirm the word exists, then walk back up cleaning up nodes that are no longer needed.

A node can be removed only if both conditions hold:

  1. It is not the end of some other word (isWord is false), and
  2. It has no remaining children.

The recursive deletion below returns true to its parent when the parent should detach this node.

Walking through an example clarifies the behavior. Suppose the trie contains design, desktop, and destination. Deleting desktop:

  1. The recursion walks down d → e → s → k → t → o → p.
  2. At p, isWord is set to false and the node has no children, so isEmpty returns true.
  3. The recursion unwinds. o, t, and k each have no other children and are not word endings, so each detaches itself from its parent.
  4. At s, the recursion sees that s still has children pointing toward design and destination. The cleanup stops there.

After the delete, the trie still contains design and destination, sharing the prefix des.

Time and Space Complexity

The table below assumes k is the length of the word, N is the total number of words stored, and σ is the size of the alphabet (26 for lowercase English).

Scroll
OperationTimeExtra Space
InsertO(k)O(k) in the worst case (all-new path)
SearchO(k)O(1)
StartsWithO(k)O(1)
DeleteO(k)O(k) recursion stack

The total space the trie itself occupies is the more interesting figure:

Children representationTotal space
Array of size σ (new TrieNode[26])O(N × k × σ)
Hash map (Map<Character, TrieNode>)O(N × k)

Array-based children give O(1) lookup per character but pay σ pointers per node, most of which are null. Hash-map-based children save memory at the cost of slower per-character lookup. For lowercase English the array form is the standard choice. For Unicode or large alphabets the hash-map form is the standard choice.

Autocomplete Walkthrough

The most common practical use of a trie is autocomplete: given a prefix, return every word that starts with it.

The implementation has two phases. First, walk down to the node at the end of the prefix using findNode. Then, do a depth-first traversal from that node, collecting every word found along the way.

Suppose the trie contains design, desktop, destination, delight, and dog. Calling autocomplete("des"):

  1. findNode("des") walks d → e → s and returns the node at s.
  2. collectWords starts at s with the prefix des already accumulated.
  3. The traversal explores both branches at s: one toward ign, the other toward ktop and tination.
  4. Each path that ends at a node with isWord = true adds the accumulated string to the results.

The output is ["design", "desktop", "destination"]. The branches under delight and dog are never visited because they sit under different prefixes.

The time complexity is O(p + m × L), where p is the length of the prefix, m is the number of matching words, and L is the average length of those matches. The autocomplete is fast specifically because the trie has already grouped all words by their prefix.

Trie vs Hash Map

Both structures support fast key lookup, but they specialize in different operations.

Scroll
FeatureTrieHash Map
Exact lookup of word of length kO(k)O(k) for hashing the key
Prefix search (startsWith)O(k)Not supported (would need to scan every key)
AutocompleteO(p + m × L)O(N × p) to scan all keys
Memory per wordHigher (one node per character)Lower (one entry per word)
Words sharing prefixesMemory is reusedEach word stored independently
Sorted iterationYes (DFS gives lexicographic order)No (hash order is random)
Use casesAutocomplete, spell-check, IP routing tables, dictionary problemsGeneral key-value storage

A useful way to think about the choice: if the problem involves prefixes or ordered word traversal, use a trie. If the problem only involves exact lookups, a hash map is simpler and uses less memory.

Quiz

Introduction Quiz

10 quizzes