Overview
- A trie (prefix tree) is a tree-like data structure that stores strings.
- It is designed for efficient retrieval of strings, especially when searching by prefixes.
Key Properties
- Each node represents a single character of the string.
- Strings are stored as paths from the root to leaf nodes.
- Common prefixes are shared among strings, making tries memory-efficient for such use cases.
Common Operations
- Insert: Add a word to the trie.
- Search: Check if a word exists in the trie.
- Starts With: Check if any word in the trie starts with a given prefix.
Python Implementation
class TrieNode:
def __init__(self):
self.children = {}
self.is_end_of_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_end_of_word = True
def search(self, word):
node = self.root
for char in word:
if char not in node.children:
return False
node = node.children[char]
return node.is_end_of_word
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
# Example Usage
trie = Trie()
trie.insert("apple")
print(trie.search("apple")) # Output: True
print(trie.search("app")) # Output: False
print(trie.starts_with("app")) # Output: True
trie.insert("app")
print(trie.search("app")) # Output: True
Applications of Tries
-
Autocomplete
- Suggest words based on a given prefix.
-
Spell Checking
- Quickly verify if a word exists in a dictionary.
-
Longest Prefix Matching
- Find the longest prefix of a string that matches a dictionary entry.
-
Word Search
- Used in problems like Boggle to efficiently search for words on a board.
-
IP Routing
- Store and lookup IP addresses efficiently.
-
Compressed Storage
- Tries are used in suffix trees for compressed string storage.
Common Trie Problems
1. Word Search II
Find all words from a given list that can be formed on a board.
def find_words(board, words):
def backtrack(node, row, col, path):
if node.is_end_of_word:
result.add(path)
node.is_end_of_word = False # Avoid duplicate results
if not (0 <= row < len(board) and 0 <= col < len(board[0])):
return
char = board[row][col]
if char not in node.children:
return
board[row][col] = "#" # Mark as visited
for dx, dy in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
backtrack(node.children[char], row + dx, col + dy, path + char)
board[row][col] = char
# Build trie
trie = Trie()
for word in words:
trie.insert(word)
result = set()
for r in range(len(board)):
for c in range(len(board[0])):
backtrack(trie.root, r, c, "")
return list(result)
# Example Usage
board = [
['o', 'a', 'a', 'n'],
['e', 't', 'a', 'e'],
['i', 'h', 'k', 'r'],
['i', 'f', 'l', 'v']
]
words = ["oath", "pea", "eat", "rain"]
print(find_words(board, words)) # Output: ["oath", "eat"]
2. Longest Word in Dictionary
Find the longest word that can be built letter by letter.
def longest_word(words):
trie = Trie()
for word in words:
trie.insert(word)
words.sort(key=lambda x: (-len(x), x)) # Sort by length and lexicographically
for word in words:
node = trie.root
if all(node.children[char].is_end_of_word for char in word):
return word
return ""
# Example Usage
words = ["w", "wo", "wor", "worl", "world"]
print(longest_word(words)) # Output: "world"
Summary
Tries are powerful for prefix-based operations and problems involving dictionaries of words. Their efficient search capabilities make them ideal for applications like autocomplete and word search.