DSA Notes
Build a real-time autocomplete system using a trie data structure with frequency ranking, prefix matching, and a complete Python implementation.
Project Overview
You know that dropdown that appears when you start typing in Google or your IDE? That is an autocomplete system. At its core, it needs to:
- Store a dictionary of valid words/phrases
- Given a prefix, find all words starting with that prefix
- Rank results by relevance (usually frequency)
- Return results fast enough for real-time use (under 50ms)
A trie (prefix tree) is the perfect data structure for this. Let me show you how to build one from scratch.
The Trie Data Structure
A trie stores strings character by character, sharing common prefixes. The word "cat" and "car" share the path c→a, then branch to t and r.
Each node contains:
- A dictionary of children (character → child node)
- A boolean marking if this node completes a word
- Optional: frequency count, the full word
Implementation
TrieNode and Trie Base
Autocomplete: Find All Words with Prefix
Usage Example
Optimization: Heap for Top-K
Instead of collecting ALL words and sorting, use a min-heap to maintain only the top K results during DFS:
Real-Time Updates: Recording Searches
A real autocomplete system updates frequencies based on actual user behavior:
def record_search(self, query):
"""User performed this search — increase its frequency."""
self.insert(query, frequency=1) # Increment by 1
def input_character(self, typed_so_far):
"""Called on every keystroke for real-time suggestions."""
if not typed_so_far:
return []
return self.autocomplete(typed_so_far, max_results=5)LeetCode Problem 642: Design Search Autocomplete System
This is based on the classic interview problem. The system tracks sentence history and user input character by character:
class AutocompleteSystem642:
def __init__(self, sentences, times):
self.root = TrieNode()
self.current_input = ""
for sentence, count in zip(sentences, times):
self.insert(sentence, count)
def input(self, c):
if c == '#':
# End of sentence — record it
self.insert(self.current_input, 1)
self.current_input = ""
return []
self.current_input += c
return self.autocomplete(self.current_input, 3)Complexity Analysis
| Operation | Time | Space |
|---|---|---|
| Insert word of length m | O(m) | O(m) per word |
| Search exact word | O(m) | O(1) |
| Autocomplete (all results) | O(n) where n = words with prefix | O(n) |
| Autocomplete (top-K with heap) | O(n log k) | O(k) |
| Total space for N words, avg length m | - | O(N × m) worst, usually much less |
Extensions for Production
- Fuzzy matching: Allow typos using edit distance at each trie node
- Personalization: Maintain per-user frequency tables
- Recency bias: Weight recent searches higher with time decay
- Phrase completion: Store full sentences, not just words
- Caching: Cache results for common prefixes (top 1000 prefixes cover 90% of queries)
Key Takeaways
- Tries excel at prefix matching — O(m) to reach any prefix regardless of dictionary size
- Frequency-ranked results require either sorting after collection or a heap during collection
- The trie shares prefixes, so "programming" and "program" share 7 nodes — very space-efficient for English
- Real autocomplete systems combine tries with caching, personalization, and machine learning ranking
Performance Testing
Let's benchmark our autocomplete system with realistic data:
Handling Special Cases
Case-Insensitive Search
Our implementation already lowercases everything. But what about preserving display case?
Multi-Word Queries
For sentence completion (like Google), store entire sentences as paths:
# Instead of character-by-character, you can also store word-by-word
# "how to cook" -> trie path: "how" -> "to" -> "cook"
# This gives word-level autocomplete for search queriesMemory Optimization with Compressed Tries (Radix Trees)
If memory is a concern, compress single-child chains into single nodes:
This reduces node count significantly for long words with unique suffixes. The tradeoff is slightly more complex insertion and search logic.
Real-World Autocomplete Architecture
Production systems like Google Search add layers beyond a simple trie:
- Distributed trie: Sharded across servers by prefix (a-f on server 1, g-m on server 2)
- Machine learning ranking: TF-IDF replaced by neural models that consider context, recency, location
- Personalization: Your search history weighs suggestions differently than others'
- Caching layer: Top 10,000 prefixes are pre-computed and cached in CDN edge servers
- Fallback: If no trie match, fall back to spell correction + popular searches
Our project implements the core algorithmic foundation that all these systems build upon.
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Autocomplete System Using Trie.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Data Structures & Algorithms topic.
Search Terms
data-structures-algorithms, data structures & algorithms, data, structures, algorithms, projects, autocomplete, system
Related Data Structures & Algorithms Topics