Prefix Search Using Trie in DSA Typescript - Time & Space Complexity
We want to understand how fast we can find words that start with a given prefix using a Trie.
The question is: how does the search time grow as the prefix length or number of words changes?
Analyze the time complexity of the following code snippet.
class TrieNode {
children: Map = new Map();
isWord: boolean = false;
}
function startsWith(root: TrieNode, prefix: string): boolean {
let node = root;
for (const char of prefix) {
if (!node.children.has(char)) return false;
node = node.children.get(char)!;
}
return true;
}
This code checks if any word in the Trie starts with the given prefix by following nodes for each prefix character.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Looping through each character of the prefix string.
- How many times: Exactly once per character in the prefix (length = m).
Each character in the prefix requires one step to check in the Trie.
| 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, not with the total number of words stored.
Time Complexity: O(m)
This means the search time depends only on how long the prefix is, not on how many words are in the Trie.
[X] Wrong: "The search time depends on the total number of words stored in the Trie."
[OK] Correct: The search only follows nodes for the prefix characters, so it depends on prefix length, not total words.
Understanding this helps you explain why Tries are efficient for prefix searches, a common real-world problem like autocomplete.
"What if we changed the prefix search to also return all words starting with the prefix? How would the time complexity change?"
Practice
Solution
Step 1: Understand Trie usage
A Trie stores words by their prefixes, allowing quick prefix-based lookups.Step 2: Identify prefix search goal
Prefix search means finding all words starting with a given prefix efficiently.Final Answer:
To efficiently find all words that start with a given prefix -> Option AQuick Check:
Trie = Prefix search [OK]
- Confusing Trie with sorting structure
- Thinking Trie stores words reversed
- Assuming Trie counts total words only
Solution
Step 1: Check children type
Children should be a Map with string keys and TrieNode values for prefix characters.Step 2: Check endOfWord type
endOfWord is a boolean indicating if node ends a word.Final Answer:
class TrieNode { children: Map; endOfWord: boolean; constructor() { this.children = new Map(); this.endOfWord = false; } } -> Option DQuick Check:
Map + boolean endOfWord = class TrieNode { children: Map; endOfWord: boolean; constructor() { this.children = new Map(); this.endOfWord = false; } } [OK]
- Using array instead of Map for children
- Using wrong key type in Map
- Setting endOfWord as string instead of boolean
console.log(trie.startsWith('app'))?
class TrieNode {
children = new Map();
endOfWord = false;
}
class Trie {
root = new TrieNode();
insert(word: string) {
let node = this.root;
for (const char of word) {
if (!node.children.has(char)) {
node.children.set(char, new TrieNode());
}
node = node.children.get(char)!;
}
node.endOfWord = true;
}
startsWith(prefix: string): boolean {
let node = this.root;
for (const char of prefix) {
if (!node.children.has(char)) return false;
node = node.children.get(char)!;
}
return true;
}
}
const trie = new Trie();
trie.insert('apple');
trie.insert('app');
trie.insert('bat');Solution
Step 1: Insert words into Trie
Words 'apple', 'app', and 'bat' are inserted, so prefix 'app' exists in Trie.Step 2: Check startsWith method
Method returns true if prefix characters exist in Trie nodes, which they do for 'app'.Final Answer:
true -> Option AQuick Check:
Prefix 'app' found = true [OK]
- Confusing startsWith with search for whole word
- Expecting false if prefix is a word itself
- Thinking startsWith returns endOfWord status
startsWith method for a Trie but always returns false. What is the bug?
startsWith(prefix: string): boolean {
let node = this.root;
for (const char of prefix) {
if (!node.children.has(char)) return false;
node = node.children.get(char);
}
return false;
}Solution
Step 1: Analyze loop behavior
The loop correctly traverses nodes for each prefix character.Step 2: Check return statement
Method returns false always after loop, so never returns true even if prefix found.Step 3: Fix return logic
Return true after loop if all prefix chars found.Final Answer:
It should return true after the loop if prefix exists -> Option CQuick Check:
Return true after loop if prefix found [OK]
- Returning false at end regardless
- Checking endOfWord unnecessarily
- Incorrect node initialization
Solution
Step 1: Understand prefix search extension
To get all words with prefix, first find node matching prefix.Step 2: Collect words recursively
From prefix node, recursively traverse children to collect all complete words.Step 3: Evaluate other options
Filtering array or hash map is less efficient; reversed words search suffixes, not prefixes.Final Answer:
Traverse to prefix node, then recursively collect all words below. -> Option BQuick Check:
Recursive traversal from prefix node = Traverse to prefix node, then recursively collect all words below. [OK]
- Filtering all words each time (slow)
- Using hash map without prefix traversal
- Confusing prefix with suffix search
Ready to prove it?
Pass the test and this pattern moves unknown
