📖 Scenario: Imagine you are building a search box for a website. When users start typing, you want to suggest words that start with what they typed. This helps users find what they want faster.
🎯 Goal: Build a simple autocomplete system using a Trie data structure. You will create the Trie, add words to it, search for words starting with a prefix, and print the suggestions.
📋 What You'll Learn
Create a TrieNode class with a dictionary for children and a boolean to mark end of word
Create a Trie class with methods to insert words and search for words starting with a prefix
Insert a list of given words into the Trie
Search for words starting with a given prefix and collect suggestions
Print the list of suggestions
💡 Why This Matters
🌍 Real World
Autocomplete systems are used in search engines, messaging apps, and code editors to help users type faster and find results quickly.
💼 Career
Understanding Tries and autocomplete logic is useful for software engineers working on search features, text processing, and user experience improvements.
Progress0 / 4 steps
1
Create TrieNode class and Trie class with Insert method
Create a class called TrieNode with a public dictionary called Children of type Dictionary<char, TrieNode> and a public boolean called IsEndOfWord. Then create a class called Trie with a public TrieNode field called Root initialized in the constructor. Add a public method Insert in Trie that takes a string word and inserts it into the Trie by creating nodes for each character and marking the last node's IsEndOfWord as true.
DSA C#
Hint
Think of TrieNode as a box holding letters and a flag if a word ends there. The Trie has a root box. Insert adds letters one by one.
2
Insert given words into the Trie
Create a list of strings called words with these exact values: "apple", "app", "application", "bat", "ball". Then create a Trie object called trie and insert each word from words into trie using the Insert method.
DSA C#
Hint
Use a list to hold the words. Then loop through the list and insert each word into the trie.
3
Add Search method to find words starting with a prefix
Add a public method Search in the Trie class that takes a string prefix and returns a list of strings. This method should find the node matching the prefix and then collect all words starting from that node by traversing the Trie. Use a helper private method CollectWords that takes a TrieNode, a string prefix, and a list of strings results to add all words starting from that node to results. The Search method returns the list of words starting with the prefix.
DSA C#
Hint
First find the node matching the prefix. Then use recursion to find all words below that node.
4
Search for prefix and print suggestions
In the Main method, call trie.Search with the prefix "app" and store the result in a variable called suggestions. Then print each word in suggestions on its own line.
DSA C#
Hint
Call Search with "app" and print each word in the returned list on its own line.
Practice
(1/5)
1. What is the main advantage of using a Trie over a HashSet for implementing an autocomplete system?
easy
A. Trie uses less memory than HashSet for all cases
B. Trie supports prefix-based search efficiently, while HashSet requires checking all words
C. Trie is faster for exact word lookup than HashSet
D. Trie automatically sorts words alphabetically unlike HashSet
Solution
Step 1: Understand autocomplete requirement
Autocomplete needs to find all words that start with a given prefix quickly.
Step 2: Compare Trie vs HashSet
Trie stores characters level by level, so prefix traversal is O(prefix length). HashSet requires scanning all words to check which ones start with the prefix.
Final Answer:
Trie supports prefix-based search efficiently, while HashSet requires checking all words -> Option B
Quick Check:
Trie = prefix search in O(m), HashSet = O(n*m) for prefix [OK]
Hint: Trie shares common prefixes among words - natural fit for autocomplete [OK]
Common Mistakes:
Assuming Trie always uses less memory - it can use more for sparse word sets
Thinking exact lookup is why Trie beats HashSet - HashSet is actually faster for exact lookup
Assuming Trie sorts automatically - it doesn't sort, traversal order depends on children insertion
2. Which C# code correctly inserts a word into a Trie node structure where each node has a Dictionary<char, TrieNode> Children and a bool IsEndOfWord?
easy
A. node.Children[c] = new TrieNode(); node = node.Children[c];
B. if (!node.Children.ContainsKey(c)) node.Children[c] = new TrieNode(); node = node.Children[c];
C. node.Children.Add(c); node = node.Children[c];
D. node.Children[c] = c; node = node.Children[c];
Solution
Step 1: Check if child exists
Before creating a new node, check if the character key already exists in Children to avoid overwriting existing subtrees.
Step 2: Create only if missing
Using ContainsKey check ensures we reuse existing nodes and only create new ones when the character path doesn't exist yet.
Final Answer:
if (!node.Children.ContainsKey(c)) node.Children[c] = new TrieNode(); node = node.Children[c]; -> Option B
Quick Check:
Always check ContainsKey before adding to avoid overwriting existing paths [OK]
Hint: Always guard with ContainsKey before inserting a new Trie node [OK]
Common Mistakes:
Option A overwrites existing children, destroying previously inserted words
Option C uses Add which throws if key already exists
Option D stores char as value instead of TrieNode
3. What will the following C# Trie prefix search return when searching for prefix "ca" in a Trie containing ["cat", "car", "card", "dog"]?
List<string> results = new List<string>();
void DFS(TrieNode node, string current) {
if (node.IsEndOfWord) results.Add(current);
foreach (var kvp in node.Children)
DFS(kvp.Value, current + kvp.Key);
}
TrieNode prefixNode = GetPrefixNode("ca");
if (prefixNode != null) DFS(prefixNode, "ca");
medium
A. ["cat", "car", "card", "dog"]
B. ["ca"]
C. ["cat", "car", "card"]
D. []
Solution
Step 1: Find prefix node
GetPrefixNode("ca") traverses c→a and returns the node at 'a'. "dog" is unreachable from this node.
Step 2: DFS from prefix node
DFS explores all children from 'a': t (cat), r (car), r→d (card). All three have IsEndOfWord=true.
Final Answer:
["cat", "car", "card"] -> Option C
Quick Check:
Only words under prefix "ca" subtree are returned, "dog" excluded [OK]
Hint: DFS from prefix node returns only words in that subtree - "dog" is in a different branch [OK]
Common Mistakes:
Including 'dog' - it's in a completely separate Trie branch
Returning only 'ca' - 'ca' itself is not marked IsEndOfWord
Returning empty - prefix 'ca' exists in the Trie so DFS finds words
4. Identify the bug in this C# autocomplete prefix search:
bool StartsWith(string prefix) {
TrieNode node = root;
foreach (char c in prefix) {
if (!node.Children.ContainsKey(c))
return true;
node = node.Children[c];
}
return false;
}
medium
A. Should return false when character not found, and true at the end
B. Should use a List instead of Dictionary for Children
C. Should check IsEndOfWord instead of ContainsKey
D. Should start from the last character of the prefix
Solution
Step 1: Trace the logic
When a character is not found in Children, it means the prefix doesn't exist - should return false, not true. And after traversing all prefix characters successfully, the prefix exists - should return true.
Step 2: Identify the swap
The return values are swapped: 'return true' and 'return false' are in each other's place.
Final Answer:
Should return false when character not found, and true at the end -> Option A
Hint: If you can't follow the path, prefix is absent = false; if path complete = true [OK]
Common Mistakes:
Thinking Dictionary is wrong - Dictionary is correct for Children
Checking IsEndOfWord - StartsWith only checks if prefix path exists, not word end
Starting from last character - prefix traversal always goes left to right
5. In a Trie-based autocomplete system with 100,000 words averaging 8 characters each, what is the time complexity to retrieve all words matching a prefix of length p, where k is the number of matching words and m is the average word length?
hard
A. O(n) where n is total number of words
B. O(p + k * m) - traverse prefix then DFS over matching words
C. O(p * n) - check prefix against each word
D. O(1) - Trie provides constant time autocomplete
Solution
Step 1: Cost of reaching prefix node
Traversing p characters to reach the prefix node costs O(p).
Step 2: Cost of DFS to collect results
DFS visits all nodes under the prefix subtree. With k matching words of average length m, this costs O(k * m) in the worst case.