Prefix Search Using Trie in DSA Javascript - Time & Space Complexity
We want to understand how fast we can find words that start with a given prefix using a Trie.
How does the search time change as the prefix or dictionary grows?
Analyze the time complexity of the following code snippet.
class TrieNode {
constructor() {
this.children = {};
this.isEnd = false;
}
}
function prefixSearch(root, prefix) {
let node = root;
for (const char of prefix) {
if (!node.children[char]) return false;
node = node.children[char];
}
return true;
}
This code checks if any word in the Trie starts with the given prefix by walking down the Trie nodes.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Looping through each character of the prefix.
- How many times: Exactly once per character in the prefix (length m).
As the prefix gets longer, the search takes more steps, one per character.
| Input Size (prefix length m) | Approx. Operations |
|---|---|
| 10 | 10 steps |
| 100 | 100 steps |
| 1000 | 1000 steps |
Pattern observation: The time grows linearly with the prefix length.
Time Complexity: O(m)
This means the search time depends only on the prefix length, not the number of words stored.
[X] Wrong: "The search time depends on the total number of words in the Trie."
[OK] Correct: The search only follows the prefix characters, so it depends on prefix length, not total words.
Knowing how prefix search scales helps you explain efficient word lookup in real apps like autocomplete.
"What if we changed the prefix search to return all words starting with the prefix? How would the time complexity change?"
Practice
Solution
Step 1: Understand Trie usage
A Trie stores words in a tree-like structure where each node represents a character.Step 2: Identify prefix search benefit
This structure allows quick traversal to find all words sharing a prefix without scanning all words.Final Answer:
To efficiently find all words starting with a given prefix -> Option AQuick Check:
Trie = prefix search efficiency [OK]
- Confusing Trie with sorting algorithms
- Thinking Trie stores numbers only
- Assuming Trie is for arithmetic
Solution
Step 1: Understand Trie node structure
A Trie node typically has a children object to hold next characters and a boolean to mark word end.Step 2: Match correct initialization
const node = { children: {}, isEnd: false }; correctly creates an object with children as empty object and isEnd as false.Final Answer:
const node = { children: {}, isEnd: false }; -> Option DQuick Check:
Trie node = object with children and isEnd [OK]
- Using array instead of object for children
- Setting node to null instead of object
- Using Map without isEnd flag
class TrieNode {
constructor() {
this.children = {};
this.isEnd = false;
}
}
class Trie {
constructor() {
this.root = new TrieNode();
}
insert(word) {
let node = this.root;
for (const char of word) {
if (!node.children[char]) {
node.children[char] = new TrieNode();
}
node = node.children[char];
}
node.isEnd = true;
}
startsWith(prefix) {
let node = this.root;
for (const char of prefix) {
if (!node.children[char]) return false;
node = node.children[char];
}
return true;
}
}
const trie = new Trie();
trie.insert('cat');
trie.insert('car');
trie.insert('dog');
console.log(trie.startsWith('ca'));Solution
Step 1: Insert words into Trie
Words 'cat', 'car', and 'dog' are inserted, creating nodes for each character.Step 2: Check prefix 'ca'
Both 'cat' and 'car' start with 'ca', so traversal finds nodes for 'c' and 'a'.Final Answer:
true -> Option AQuick Check:
Prefix 'ca' exists = true [OK]
- Confusing startsWith with exact word search
- Expecting false if prefix is partial
- Ignoring that multiple words share prefix
insert(word) {
let node = this.root;
for (const char of word) {
if (node.children[char]) {
node.children[char] = new TrieNode();
}
node = node.children[char];
}
node.isEnd = true;
}Solution
Step 1: Analyze insert method logic
The code creates a new node only if node.children[char] exists, which is incorrect.Step 2: Correct condition for node creation
We should create a new node only if node.children[char] does NOT exist to avoid overwriting existing nodes.Final Answer:
Change if condition to check if child does NOT exist before creating new node -> Option BQuick Check:
Insert new node only if missing [OK]
- Creating new node when child already exists
- Forgetting to mark end of word
- Initializing node incorrectly
Solution
Step 1: Build Trie with all words
Insert all words so the Trie contains complete data for prefix search.Step 2: Traverse Trie for prefix 'ap'
Navigate nodes for 'a' then 'p', then collect all words from this subtree.Step 3: Collect descendant words
Gather words like 'apple', 'app', and 'apricot' that share the prefix.Final Answer:
Insert all words into Trie, then traverse nodes for prefix 'ap' and collect all descendant words -> Option CQuick Check:
Trie + prefix traversal = all matching words [OK]
- Filtering list without Trie defeats purpose
- Ignoring words not starting with prefix during insertion
- Using hash map instead of Trie for prefix search
Ready to prove it?
Pass the test and this pattern moves unknown
