Discover why your phone's autocomplete is so fast and how data structures make it possible!
Trie vs Hash Map for Prefix Matching in DSA Rust - Why the Distinction Matters
Imagine you have a huge phone book and you want to find all contacts whose names start with "Jo". You try to look through every name one by one to find matches.
Going through every name manually is slow and tiring. If the list is very long, it takes a lot of time and you might miss some matches or make mistakes.
A Trie organizes words by their letters, so you can quickly jump to all names starting with "Jo" without checking every name. A Hash Map can store words but doesn't help find all words sharing the same start easily.
use std::collections::HashMap; let mut map = HashMap::new(); map.insert("John", 1); map.insert("Joanna", 2); // To find prefix "Jo", check all keys one by one
let mut trie = Trie::new(); trie.insert("John"); trie.insert("Joanna"); // To find prefix "Jo", directly get all words starting with "Jo"
It enables lightning-fast search for all words sharing a prefix, making autocomplete and prefix queries smooth and efficient.
When you type in a search bar and suggestions appear instantly, a Trie helps find all words starting with what you typed so far.
Manual search checks every word, which is slow for large data.
Trie stores words by letters, enabling quick prefix searches.
Hash Map is good for exact matches but not for prefix queries.
Practice
Solution
Step 1: Understand prefix matching requirements
Prefix matching needs to find all strings starting with a given prefix efficiently.Step 2: Compare Trie and Hash Map for prefix queries
Trie stores common prefixes once and allows traversal by characters, making prefix queries fast. Hash Map is good for exact key lookup but not for prefix searches.Final Answer:
Trie, because it stores common prefixes once and allows fast prefix queries -> Option DQuick Check:
Prefix matching = Trie [OK]
- Thinking Hash Map supports prefix queries efficiently
- Confusing exact lookup with prefix search
- Assuming arrays or lists are good for prefix matching
Solution
Step 1: Check correct HashMap declaration syntax in Rust
Rust requires specifying types inside angle brackets and calling HashMap::new() without parameters.Step 2: Validate each option
let map: std::collections::HashMap = std::collections::HashMap::new(); correctly declares a HashMap with String keys and i32 values using full path and new().Final Answer:
let map: std::collections::HashMap = std::collections::HashMap::new(); -> Option CQuick Check:
Correct Rust HashMap syntax = let map: std::collections::HashMap = std::collections::HashMap::new(); [OK]
- Using parentheses instead of ::new()
- Swapping key and value types
- Omitting type annotations when needed
struct TrieNode {
children: std::collections::HashMap,
is_end: bool,
}
impl TrieNode {
fn new() -> Self {
TrieNode { children: std::collections::HashMap::new(), is_end: false }
}
}
fn main() {
let mut root = TrieNode::new();
// Insert "cat"
let mut node = &mut root;
for c in "cat".chars() {
node = node.children.entry(c).or_insert(TrieNode::new());
}
node.is_end = true;
// Check prefix "ca"
let mut node = &root;
let mut found = true;
for c in "ca".chars() {
if let Some(next) = node.children.get(&c) {
node = next;
} else {
found = false;
break;
}
}
println!("{}", found);
}Solution
Step 1: Analyze TrieNode struct definition
TrieNode { children: HashMap, ... } creates a recursive type without indirection (e.g., Box<TrieNode>). Rust cannot compute the size of TrieNode, resulting in a compilation error.Step 2: Confirm error prevents execution
Code fails to compile, so no output or runtime behavior occurs.Final Answer:
Compilation error -> Option BQuick Check:
Recursive TrieNode without Box = compile error [OK]
- Assuming prefix must be a complete word
- Confusing is_end with prefix existence
- Ignoring Rust recursive type size requirements
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert("apple", 1);
map.insert("app", 2);
let prefix = "ap";
let mut found = false;
for key in map.keys() {
if key.starts_with(prefix) {
found = true;
break;
}
}
println!("{}", found);
}Solution
Step 1: Analyze prefix matching approach
The code scans all keys linearly and checks if they start with the prefix, which works but is inefficient for large data.Step 2: Identify the bug type
There is no syntax or runtime error, but the approach is inefficient compared to Trie which supports fast prefix queries.Final Answer:
Inefficient prefix search due to linear scan over keys -> Option AQuick Check:
Linear scan for prefix = inefficient [OK]
- Thinking code has syntax or runtime errors
- Assuming HashMap keys are ordered
- Ignoring performance implications
// Option A: Use a Trie with HashMap children // Option B: Use a HashMap storing all words as keys // Option C: Use a sorted Vec and binary search for prefix // Option D: Use a LinkedList of words and scan linearly
Solution
Step 1: Evaluate insertion and prefix search efficiency
Trie allows insertion and prefix search in O(k) time, where k is prefix length, by sharing common prefixes.Step 2: Compare other data structures
HashMap is fast for exact keys but slow for prefix search. Sorted Vec requires complex prefix range search. LinkedList is slow for prefix search.Final Answer:
Trie, because it shares prefixes and allows O(k) prefix search where k is prefix length -> Option AQuick Check:
Fast prefix search = Trie [OK]
- Choosing HashMap for prefix search
- Ignoring complexity of prefix range search in sorted Vec
- Underestimating LinkedList search cost
Ready to prove it?
Pass the test and this pattern moves unknown
