Autocomplete System with Trie in DSA Python - Time & Space Complexity
We want to understand how fast an autocomplete system using a trie can find suggestions.
How does the time to find words grow as we add more words or type more letters?
Analyze the time complexity of the following code snippet.
class TrieNode:
def __init__(self):
self.children = {}
self.is_word = False
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, word):
node = self.root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
node.is_word = True
def search_prefix(self, prefix):
node = self.root
for char in prefix:
if char not in node.children:
return None
node = node.children[char]
return node
This code builds a trie and searches for the node matching a prefix typed by the user.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Looping through each character of the input word or prefix.
- How many times: Once per character in the word or prefix (length = n).
As the prefix length grows, the search checks more characters one by one.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 10 character checks |
| 100 | 100 character checks |
| 1000 | 1000 character checks |
Pattern observation: The time grows linearly with the length of the prefix typed.
Time Complexity: O(n)
This means the time to find the prefix node grows directly with the number of characters typed.
[X] Wrong: "Searching a prefix in a trie takes time proportional to the total number of words stored."
[OK] Correct: The search only follows the path for the prefix characters, so it depends on prefix length, not total words.
Understanding trie search time helps you explain how autocomplete suggestions appear quickly as you type.
"What if we added a function to collect all words under the prefix node? How would that affect the time complexity?"
Practice
Trie in an autocomplete system?Solution
Step 1: Understand the role of Trie in prefix search
A Trie is a tree-like data structure designed to store strings by their prefixes, allowing quick prefix-based searches.Step 2: Connect Trie usage to autocomplete
Autocomplete requires finding all words starting with a given prefix efficiently, which Trie supports by traversing nodes matching the prefix.Final Answer:
To efficiently store and search prefixes of words -> Option BQuick Check:
Trie = prefix search [OK]
- Thinking Trie sorts words alphabetically
- Confusing Trie with hash tables
- Assuming Trie encrypts data
"cat" into a Trie node in Python?Solution
Step 1: Understand Trie node structure
Trie nodes typically have a dictionary calledchildrento map characters to child nodes.Step 2: Insert character as a key in children dictionary
To insert 'c', assign a new TrieNode tonode.children['c'].Final Answer:
node.children['c'] = TrieNode() -> Option DQuick Check:
Insert char in children dict = node.children['c'] = TrieNode() [OK]
- Trying to assign directly to node['c'] without children
- Using non-existent methods like add or insert
- Confusing list with dictionary for children
class TrieNode:
def __init__(self):
self.children = {}
self.is_end = False
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, word):
node = self.root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
node.is_end = True
def search_prefix(self, prefix):
node = self.root
for char in prefix:
if char not in node.children:
return False
node = node.children[char]
return True
trie = Trie()
trie.insert('dog')
trie.insert('deer')
print(trie.search_prefix('de'))
print(trie.search_prefix('do'))
print(trie.search_prefix('da'))Solution
Step 1: Insert words 'dog' and 'deer' into Trie
Both words add nodes for their characters. 'dog' adds d->o->g, 'deer' adds d->e->e->r.Step 2: Check prefixes 'de', 'do', and 'da'
'de' exists as prefix in 'deer', so returns True. 'do' exists as prefix in 'dog', so True. 'da' does not exist, so False.Final Answer:
True True False -> Option CQuick Check:
Prefix 'de' and 'do' found, 'da' not found = True, True, False [OK]
- Confusing search_prefix with full word search
- Assuming 'da' prefix exists
- Ignoring is_end flag for prefix search
class TrieNode:
def __init__(self):
self.children = []
self.is_end = False
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, word):
node = self.root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]Solution
Step 1: Identify data structure used for children
The code uses a list forchildren, but tries to access it with string keys.Step 2: Understand list indexing rules
Lists require integer indices, but code usesnode.children[char]where char is a string, causing TypeError.Final Answer:
TypeError: list indices must be integers or slices, not str -> Option AQuick Check:
Using list with string keys causes TypeError [OK]
- Using list instead of dict for children
- Ignoring error message details
- Assuming code runs without error
Solution
Step 1: Reach the node matching the prefix
Traverse the Trie to the node representing the last character of the prefix.Step 2: Collect all words starting from prefix node using DFS
Use DFS to explore all child nodes recursively, collecting words whereis_endis True to get complete words.Final Answer:
Perform a depth-first search (DFS) from the prefix node, collecting words where is_end is True -> Option AQuick Check:
DFS from prefix node with is_end check = correct autocomplete [OK]
- Returning only immediate children keys
- Ignoring is_end flags and returning incomplete words
- Using BFS without checking word ends
Ready to prove it?
Pass the test and this pattern moves unknown
