Trie vs Hash Map for Prefix Matching in DSA Python - Complexity Comparison
We want to understand how fast prefix matching works using two common data structures: Trie and Hash Map.
Which one handles searching prefixes more efficiently as the input grows?
Analyze the time complexity of prefix search using a Trie and a Hash Map.
# Trie 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]
node.is_end = True
def starts_with(self, prefix):
node = self.root
for char in prefix:
if char not in node.children:
return False
node = node.children[char]
return True
# Hash Map prefix search (naive)
words = ["apple", "app", "banana", "bat"]
# To check prefix, iterate all words and check start
prefix = "ap"
result = any(word.startswith(prefix) for word in words)
The code shows how Trie searches prefix by traversing nodes, while Hash Map checks each word's start.
Look at what repeats when searching prefixes.
- Trie: Loop over each character in the prefix once.
- Hash Map: Loop over all words, then for each word, check characters up to prefix length.
- Dominant operation: Hash Map checks many words, Trie only follows prefix path.
As input grows, how does the work change?
| Input Size (n = number of words) | Approx. Operations Trie | Approx. Operations Hash Map |
|---|---|---|
| 10 | Check ~prefix length (e.g., 2-5) | Check 10 words x prefix length |
| 100 | Still check prefix length only | Check 100 words x prefix length |
| 1000 | Still check prefix length only | Check 1000 words x prefix length |
Trie work depends only on prefix length, stays small. Hash Map work grows with number of words.
Time Complexity: O(m) for Trie, O(n * m) for Hash Map
Trie searches prefix in time proportional to prefix length (m), while Hash Map checks all words (n) times prefix length.
[X] Wrong: "Hash Map prefix search is always faster because hash lookups are quick."
[OK] Correct: Hash Map keys are whole words, not prefixes, so checking prefixes requires scanning many words, making it slower as word count grows.
Understanding how different data structures handle prefix search helps you choose the right tool and explain your choice clearly in interviews.
What if we stored all prefixes as keys in the Hash Map? How would that affect the time complexity?
Practice
Solution
Step 1: Understand prefix matching requirement
Prefix matching means checking if any string starts with a given prefix efficiently.Step 2: Compare Trie and Hash Map for prefix search
Trie stores prefixes explicitly in its nodes, allowing quick traversal to check prefixes. Hash Map stores full strings as keys, so prefix search requires checking multiple keys or extra processing.Final Answer:
Trie, because it stores prefixes explicitly for fast lookup -> Option AQuick Check:
Prefix search = Trie [OK]
- Thinking hash map keys support prefix search directly
- Confusing prefix search with exact match
- Assuming arrays or linked lists are efficient for prefix search
Solution
Step 1: Understand Trie node structure
Each Trie node typically has a dictionary called children to store next characters as keys.Step 2: Insert character at position i
To insert a character, assign a new TrieNode to node.children[word[i]].Final Answer:
node.children[word[i]] = TrieNode() -> Option DQuick Check:
Insert char = node.children[word[i]] = TrieNode() [OK]
- Using node[word] = True for whole word
- Using node[word[i]] = TrieNode() without .children
- Calling non-existent add() method
class TrieNode:
def __init__(self):
self.children = {}
self.end = False
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, word):
node = self.root
for ch in word:
if ch not in node.children:
node.children[ch] = TrieNode()
node = node.children[ch]
node.end = True
def starts_with(self, prefix):
node = self.root
for ch in prefix:
if ch not in node.children:
return False
node = node.children[ch]
return True
trie = Trie()
trie.insert('apple')
print(trie.starts_with('app'))
print(trie.starts_with('apl'))Solution
Step 1: Insert 'apple' into Trie
Each character 'a', 'p', 'p', 'l', 'e' is added as nodes. The end flag is set at 'e'.Step 2: Check starts_with for 'app' and 'apl'
'app' exists as prefix nodes, so returns True. 'apl' fails at 'l' because 'l' is child of 'p' only after 'pp', so returns False.Final Answer:
True False -> Option BQuick Check:
Prefix 'app' found = True, 'apl' not found = False [OK]
- Assuming 'apl' is prefix of 'apple'
- Confusing end flag with prefix existence
- Mixing up True/False order in output
words = {'apple': 1, 'app': 2, 'banana': 3}
prefix = 'ap'
if prefix in words:
print('Prefix found')
else:
print('Prefix not found')Solution
Step 1: Understand hash map key lookup
Checking 'prefix in words' looks for exact key match, not prefix match.Step 2: Identify prefix matching limitation
Hash maps do not support partial key matching; prefix must be checked by iterating keys or using a Trie.Final Answer:
Hash map keys do not support prefix matching directly -> Option CQuick Check:
Prefix search in hash map = no direct support [OK]
- Thinking 'prefix in dict' checks partial keys
- Assuming syntax error causes failure
- Ignoring need for iteration or Trie for prefix
Solution
Step 1: Analyze each data structure for prefix search
Hash map requires checking all keys for prefix, which is slow for 1 million words. List and linked list require scanning all words, also slow.Step 2: Understand Trie advantage
Trie stores prefixes as nodes, so traversing to prefix node is fast and does not require scanning all words.Final Answer:
Trie allows direct traversal to prefix node, fast for large data -> Option AQuick Check:
Large prefix search = Trie traversal [OK]
- Choosing hash map or list for large prefix search
- Ignoring Trie prefix traversal speed
- Assuming linked list is efficient for prefix search
Ready to prove it?
Pass the test and this pattern moves unknown
