Trie (Prefix Tree)
Trie (pronounced “try”), also known as a Prefix Tree, is a specialized tree data structure designed for efficient prefix-based operations on strings. Unlike binary search trees (BSTs) or hash tables, Tries organize data by character prefixes, making them ideal for tasks like autocomplete, spell checking, and prefix matching. Each node in a Trie represents a single character, and paths from the root to leaf nodes form complete strings.
Tries are widely used in search engines, text editors, and dictionary applications—where fast prefix lookups and string insertion/deletion are critical.
Core Concepts & Structure
1. Trie Node
Each node in a Trie contains two key components:
- Children: A dictionary (or array) mapping characters to child nodes (e.g.,
{'a': Node, 'b': Node}). - Is End of Word: A boolean flag (
is_end) indicating if the node marks the end of a complete string (e.g., the node for ‘t’ in “cat” hasis_end = True).
2. Trie Structure Rules
- The root node is empty (no character) and has no parent.
- Each path from the root to a node with
is_end = Truerepresents a valid string stored in the Trie. - Characters are shared across common prefixes (e.g., “cat”, “car”, and “cart” share the prefix “ca”).
Example Trie (Storing [“cat”, “car”, “cart”, “dog”, “do”])
plaintext
Root (is_end=False)
/ \
c (F) d (F)
/ /
a (F) o (T) → "do"
/ \
t (T) r (F) → "cat"
\
t (T) → "cart"
- Paths:
- Root → c → a → t → “cat” (is_end=True)
- Root → c → a → r → t → “cart” (is_end=True)
- Root → d → o → “do” (is_end=True)
- Root → d → o → g → “dog” (is_end=True)
Key Operations
Tries support 5 core operations, all optimized for prefix efficiency:
- Insert: Add a string to the Trie.
- Search: Check if a string exists in the Trie.
- Prefix Search: Check if any string in the Trie starts with a given prefix.
- Delete: Remove a string from the Trie.
- Autocomplete: Retrieve all strings in the Trie that start with a given prefix.
Trie Implementation (Python)
We’ll implement a Trie with a TrieNode class and a Trie class encapsulating the core operations. We use dictionaries for children (flexible for all characters) and support case-sensitive strings (easily modified for case insensitivity).
Full Implementation
python
运行
class TrieNode:
"""Node class for Trie (Prefix Tree)."""
def __init__(self):
self.children = {} # Key: character, Value: TrieNode
self.is_end = False # Marks end of a complete string
class Trie:
def __init__(self):
self.root = TrieNode() # Root node (empty character)
# -------------------------- Insert Operation --------------------------
def insert(self, word: str) -> None:
"""Insert a string into the Trie."""
current = self.root
for char in word:
# Create new node if character not in children
if char not in current.children:
current.children[char] = TrieNode()
# Move to the child node
current = current.children[char]
# Mark the end of the word
current.is_end = True
# -------------------------- Search Operation --------------------------
def search(self, word: str) -> bool:
"""Check if a complete string exists in the Trie."""
current = self.root
for char in word:
if char not in current.children:
return False # Character not found—word doesn't exist
current = current.children[char]
# Return True only if the node marks the end of a word
return current.is_end
# -------------------------- Prefix Search Operation --------------------------
def starts_with(self, prefix: str) -> bool:
"""Check if any string in the Trie starts with the given prefix."""
current = self.root
for char in prefix:
if char not in current.children:
return False # Prefix not found
current = current.children[char]
# Prefix exists (no need to check is_end)
return True
# -------------------------- Autocomplete Operation --------------------------
def autocomplete(self, prefix: str) -> list:
"""Retrieve all strings in the Trie that start with the given prefix."""
current = self.root
# First, traverse to the end of the prefix
for char in prefix:
if char not in current.children:
return [] # Prefix not found—no matches
current = current.children[char]
# Recursively collect all words starting from the prefix node
def collect_words(node, current_word, result):
if node.is_end:
result.append(current_word)
# Traverse all children to collect words
for char, child_node in node.children.items():
collect_words(child_node, current_word + char, result)
result = []
collect_words(current, prefix, result)
return result
# -------------------------- Delete Operation --------------------------
def delete(self, word: str) -> bool:
"""Delete a string from the Trie. Return True if deleted, False if not found."""
# Recursive helper to handle deletion (tracks if node can be pruned)
def _delete_helper(node, word, index) -> bool:
# Base case: Reached end of word
if index == len(word):
# Word not found (node doesn't mark end of word)
if not node.is_end:
return False
# Unmark end of word
node.is_end = False
# Return True if node has no children (can be pruned)
return len(node.children) == 0
char = word[index]
# Character not found—word doesn't exist
if char not in node.children:
return False
# Recursively delete the next character
can_prune = _delete_helper(node.children[char], word, index + 1)
# If child can be pruned, remove it from current node's children
if can_prune:
del node.children[char]
# Return True if current node has no children and is not end of another word
return len(node.children) == 0 and not node.is_end
return False
return _delete_helper(self.root, word, 0)
# Test the Trie implementation
if __name__ == "__main__":
# Initialize Trie
trie = Trie()
# Insert words
words = ["cat", "car", "cart", "dog", "do", "apple", "app"]
for word in words:
trie.insert(word)
# Search
print("=== Search Tests ===")
print(f"Search 'cat': {trie.search('cat')}") # Output: True
print(f"Search 'car': {trie.search('car')}") # Output: False (not marked as end)
print(f"Search 'cart': {trie.search('cart')}") # Output: True
print(f"Search 'do': {trie.search('do')}") # Output: True
print(f"Search 'dog': {trie.search('dog')}") # Output: True
print(f"Search 'apple': {trie.search('apple')}")# Output: True
print(f"Search 'app': {trie.search('app')}") # Output: True
print(f"Search 'ap': {trie.search('ap')}") # Output: False (not end of word)
# Prefix Check
print("\n=== Prefix Tests ===")
print(f"Starts with 'ca': {trie.starts_with('ca')}") # Output: True
print(f"Starts with 'app': {trie.starts_with('app')}")# Output: True
print(f"Starts with 'xyz': {trie.starts_with('xyz')}")# Output: False
# Autocomplete
print("\n=== Autocomplete Tests ===")
print(f"Autocomplete 'ca': {trie.autocomplete('ca')}")# Output: ['cat', 'cart']
print(f"Autocomplete 'app': {trie.autocomplete('app')}")# Output: ['app', 'apple']
print(f"Autocomplete 'd': {trie.autocomplete('d')}") # Output: ['do', 'dog']
# Delete
print("\n=== Delete Tests ===")
trie.delete("cart")
print(f"After deleting 'cart': Autocomplete 'ca' → {trie.autocomplete('ca')}")# Output: ['cat']
trie.delete("app")
print(f"After deleting 'app': Search 'app' → {trie.search('app')}")# Output: False
print(f"After deleting 'app': Autocomplete 'app' → {trie.autocomplete('app')}")# Output: ['apple']
Time and Space Complexity
Time Complexity
All operations depend on the length of the string/prefix (L) and the number of strings (n):
| Operation | Time Complexity | Explanation |
|---|---|---|
| Insert | O(L) | Traverse L characters to insert the string (each step is O(1) dictionary access). |
| Search | O(L) | Traverse L characters to check if the string exists. |
| Starts With | O(L) | Traverse L characters to check if the prefix exists. |
| Autocomplete | O(L + K) | O(L) to reach the prefix node, O(K) to collect all K matching strings. |
| Delete | O(L) | Traverse L characters to find the string, then backtrack to prune nodes. |
Space Complexity
- Worst Case: O(n × L)Each string of length
Lis stored as a unique path (no shared prefixes, e.g., “a”, “b”, “c” → O(n×1); “apple”, “banana”, “cherry” → O(n×L)). - Best Case: O(n × L) (shared prefixes reduce redundant storage, e.g., “cat”, “car”, “cart” share “ca” → O(3 + 1 + 1) = O(5) instead of O(3+3+4)=O(10)).
Key Note: Space Efficiency
Tries are space-efficient for datasets with common prefixes (e.g., English words, URLs) but inefficient for random strings with no shared prefixes (e.g., “xyz123”, “abc789”).
Pros and Cons of Tries
Pros
- Fast Prefix Operations: Autocomplete, spell checking, and prefix matching are O(L) — faster than hash tables (O(n) for prefix searches) or BSTs (O(n log n)).
- No Collisions: Unlike hash tables, Tries avoid collisions (no need for collision resolution like chaining or open addressing).
- Lexicographical Order: Autocomplete results are naturally sorted lexicographically (no extra sorting needed).
- Efficient Deletion: Can prune unused nodes after deletion (saves space for dynamic datasets).
Cons
- High Space Overhead: For strings with no shared prefixes, Tries use more memory than hash tables (each character is a node with a dictionary of children).
- Slow for Random Access: Not optimized for random string lookups (hash tables are O(1) for exact matches).
- Complex Implementation: More complex to implement than hash tables or BSTs (especially deletion and autocomplete).
- Inefficient for Short Strings: For very short strings (e.g., single characters), hash tables are more space-efficient.
Real-World Applications of Tries
- Autocomplete: Search engines (Google, Bing), text editors (VS Code, Word), and messaging apps (WhatsApp, Telegram) use Tries to suggest words/phrases as users type.
- Spell Checkers: Tools like Grammarly or Microsoft Word use Tries to quickly check if a word exists in a dictionary and suggest corrections (e.g., “cat” vs. “cot”).
- IP Routing: Routers use Tries (called “radix trees”) to store IP addresses and route packets efficiently (prefix matching for subnets).
- Dictionary & Phone Directories: Tries organize words/phone numbers by prefix for fast lookup (e.g., finding all contacts starting with “555-“).
- DNA Sequence Analysis: Bioinformatics tools use Tries to store and search DNA sequences (prefix/suffix matching for gene analysis).
- URL Shorteners: Services like Bitly use Tries to store and resolve short URLs (prefix matching for routing).
Trie vs. Hash Table vs. BST (Key Differences)
| Feature | Trie | Hash Table | BST (Balanced) |
|---|---|---|---|
| Prefix Matching | O(L) (optimal) | O(n) (must scan all keys) | O(n log n) (in-order traversal) |
| Exact String Search | O(L) | O(1) (average case) | O(log n) |
| Space Efficiency | Good (shared prefixes) | Best (no node overhead) | Moderate (tree structure) |
| Lexicographical Order | Natural (no sorting) | No (requires extra sorting) | Yes (in-order traversal) |
| Collision Risk | None | Yes (needs resolution) | None (ordered structure) |
Summary
Best use cases: Autocomplete, spell checking, IP routing, and dictionary applications.
A Trie (Prefix Tree) is a tree data structure optimized for prefix-based string operations.
Core operations (insert, search, starts_with) run in O(L) time (L = length of string/prefix).
Key strengths: Fast autocomplete, no collisions, lexicographical order.
Key weaknesses: High space overhead for non-shared prefixes, complex implementation.
- iPhone 15 Pro Review: Ultimate Features and Specs
- iPhone 15 Pro Max: Key Features and Specifications
- iPhone 16: Features, Specs, and Innovations
- iPhone 16 Plus: Key Features & Specs
- iPhone 16 Pro: Premium Features & Specs Explained
- iPhone 16 Pro Max: Features & Innovations Explained
- iPhone 17 Pro: Features and Innovations Explained
- iPhone 17 Review: Features, Specs, and Innovations
- iPhone Air Concept: Mid-Range Power & Portability
- iPhone 13 Pro Max Review: Features, Specs & Performance
- iPhone SE Review: Budget Performance Unpacked
- iPhone 14 Review: Key Features and Upgrades
- Apple iPhone 14 Plus: The Ultimate Mid-range 5G Smartphone
- iPhone 14 Pro: Key Features and Innovations Explained
- Why the iPhone 14 Pro Max Redefines Smartphone Technology
- iPhone 15 Review: Key Features and Specs
- iPhone 15 Plus: Key Features and Specs Explained
- iPhone 12 Mini Review: Compact Powerhouse Unleashed
- iPhone 12: Key Features and Specs Unveiled
- iPhone 12 Pro: Premium Features and 5G Connectivity
- Why the iPhone 12 Pro Max is a Top Choice in 2023
- iPhone 13 Mini: Compact Powerhouse in Your Hand
- iPhone 13: Key Features and Specs Overview
- iPhone 13 Pro Review: Features and Specifications






















Leave a comment