Insert and Search in a Trie
Table of Contents + β
So you built a trie. But a tree just sitting there does nothing useful. You need to put words into it and pull answers back out. That is what the three main trie operations do. In this tutorial we will write insert, search, and startsWith step by step. Then we will run them in five languages.
Think of a phone contact list. You type a few letters and it shows every name that starts with those letters. That is exactly what a trie does. A trie is a tree that stores words one character at a time along a path.
π― The Three Operations
A trie really only needs three things you can ask it to do. Here is the short version first.
insert(word)adds a word into the trie. It walks one character at a time and builds any path that is missing. Then it marks the last node as the end of a word.search(word)checks if the full word is stored. It walks the path. At the end it checks one flag that says βa word ended hereβ.startsWith(prefix)checks if any word begins with the given letters. It just walks the path and reports whether the path exists. It does not care about the end flag.
The gap between search and startsWith is the part students mix up the most. So let us be clear about it.
Note
search("car") returns true only if βcarβ was inserted as a complete word. startsWith("car") returns true if βcarβ sits along the path to any word, like βcardβ or βcartβ, even when βcarβ itself was never inserted. Search needs the end flag. Prefix matching does not.
π§± What One Node Holds
Before the operations, picture a single node. Each node holds links to its children plus a flag.
- A map (or array) of children. There is one slot per possible next character.
- A boolean
isEndthat is true when some word finishes right at this node.
This little diagram shows the words βcarβ, βcardβ, and βcatβ living together in one trie.
See how βcarβ and βcardβ share the same path up to βrβ? The node at βrβ has its end flag on. The node βdβ hangs off it with its own end flag. That sharing is why a trie saves space.
πͺ Steps to Insert a Word
Insert builds the path. Here is the order it follows.
- Start at the root node.
- Take the first character of the word.
- If the current node has no child for that character, create a new empty node for it.
- Move down into that child node.
- Repeat for every remaining character.
- After the last character, set
isEndto true on the node you landed on.
π Steps to Search a Word
Search walks the same path. But it creates nothing.
- Start at the root node.
- For each character, look for a matching child.
- If a character has no matching child, the word is not here, so return false.
- After the last character, return the
isEndflag of the node you landed on.
π Steps for startsWith
Prefix matching is search without the final flag check.
- Start at the root node.
- For each character of the prefix, look for a matching child.
- If any character has no matching child, return false.
- If you reach the end of the prefix, return true. You do not check
isEnd.
π» The Code
Now let us put all three together. We insert a few words. Then we search for hits and misses. Then we run a prefix check. Watch the output to see how search and startsWith differ.
class Node: # One trie node: a dict of children and an end flag def __init__(self): self.children = {} self.is_end = False
class Trie: def __init__(self): self.root = Node()
# Walk the word and build any missing path, then mark the end def insert(self, word): cur = self.root for ch in word: if ch not in cur.children: cur.children[ch] = Node() cur = cur.children[ch] cur.is_end = True
# Walk the path; return the end flag of the last node def search(self, word): cur = self.root for ch in word: if ch not in cur.children: return False cur = cur.children[ch] return cur.is_end
# Walk the path; true if it exists, ignore the end flag def starts_with(self, prefix): cur = self.root for ch in prefix: if ch not in cur.children: return False cur = cur.children[ch] return True
trie = Trie()trie.insert("car")trie.insert("card")trie.insert("cat")
print("search car :", trie.search("car"))print("search care :", trie.search("care"))print("startsWith ca :", trie.starts_with("ca"))print("startsWith do :", trie.starts_with("do"))The output of the above code will be:
search car : Truesearch care : FalsestartsWith ca : TruestartsWith do : FalseRead that output slowly. search("care") is false because we never inserted βcareβ as a whole word. But startsWith("ca") is true because βcarβ, βcardβ, and βcatβ all start with βcaβ. That is the exact gap between the two operations.
β±οΈ How Fast Is It?
All three operations have the same cost, and it is easy to reason about. Each one walks the word one character at a time. It does a tiny constant amount of work per character.
Tip
Every operation runs in O(L) time, where L is the length of the word or prefix. The number of words already in the trie does not slow it down at all. A trie holding a million words searches a five-letter word in the same time as a trie holding ten words.
Here is the cost of each operation side by side.
| Operation | Time | What L means |
|---|---|---|
insert(word) | O(L) | length of the word |
search(word) | O(L) | length of the word |
startsWith(prefix) | O(L) | length of the prefix |
Caution
A common mistake is making search return true the moment the path exists. That is wrong. The path for βcarβ exists even when only βcardβ was inserted. Search must check the isEnd flag at the final node. Only startsWith is allowed to skip that check.
π§© What Youβve Learned
- β
A trie supports three core operations:
insert,search, andstartsWith. - β
insertwalks the word, creates any missing nodes, and marks the last node with an end flag. - β
searchwalks the path and returns the end flag, so it is true only for complete inserted words. - β
startsWithwalks the path and returns true if it exists, ignoring the end flag, which is how autocomplete works. - β All three run in O(L) time, where L is the length of the word or prefix, no matter how many words the trie holds.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What does insert do after walking to the last character of a word?
Why: Insert builds the path and then marks the final node's isEnd flag so a later search knows a word ends there.
- 2
You inserted only "card". What does search("car") return?
Why: The path exists, but the isEnd flag at "r" is false, so search correctly returns false.
- 3
What is the key difference between search and startsWith?
Why: Both walk the same path, but only search checks the end flag; startsWith just confirms the path exists.
- 4
What is the time complexity of searching a word of length L in a trie?
Why: Search walks one character at a time, so it costs O(L) and does not depend on how many words are stored.