Implement a Trie (Prefix Tree) in Python

problem-solving
Published

September 18, 2024

Trie, also known as a prefix tree, is a tree-like data structure used for efficient retrieval of keys in a set, often with applications in autocompletion, spell-checking, and IP routing. This post will guide you through implementing a Trie in Python, explaining the concepts and providing practical code examples.

Understanding the Trie Data Structure

A Trie’s core strength lies in its ability to store strings in a way that allows for rapid prefix-based searches. Each node in the Trie represents a character in a string. The root node represents an empty string. Paths from the root to a node represent prefixes, and paths to leaf nodes represent complete strings. Nodes usually contain:

  • Children: A dictionary mapping characters to child nodes.
  • Is_end_of_word: A boolean flag indicating whether the current node represents the end of a valid word.

Python Implementation

Let’s build a Trie class in Python:

class TrieNode:
    def __init__(self):
        self.children = {}  # Dictionary to store child nodes
        self.is_end_of_word = False


class Trie:
    def __init__(self):
        self.root = TrieNode()

    def insert(self, word):
        """Inserts a word into the Trie."""
        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):
        """Searches for a word in the Trie."""
        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):
        """Checks if any words start with the given prefix."""
        node = self.root
        for char in prefix:
            if char not in node.children:
                return False
            node = node.children[char]
        return True

Usage Examples

Let’s test our Trie implementation:

trie = Trie()
trie.insert("apple")
trie.insert("app")
trie.insert("banana")
trie.insert("bat")


print(trie.search("apple"))  # Output: True
print(trie.search("app"))  # Output: True
print(trie.search("ap"))  # Output: False
print(trie.starts_with("app"))  # Output: True
print(trie.starts_with("ba"))  # Output: True
print(trie.starts_with("ca")) # Output: False

This code demonstrates the basic functionalities of insertion, searching for a complete word, and checking for prefixes. You can expand upon this foundation to implement more complex Trie operations, such as deleting words or finding all words starting with a given prefix. Remember to handle edge cases such as empty strings appropriately in a production environment.

Optimizations and Enhancements

For larger datasets, consider optimizing your Trie implementation. Techniques like using more efficient data structures for the children dictionary (e.g., a custom array-based implementation for known character sets) can improve performance. You could also look at using more compact node representations to minimize memory usage.

Applications of Tries

The versatility of Tries makes them useful across various domains. Consider these applications:

  • Autocomplete: Suggesting words as a user types.
  • Spell Checking: Identifying misspelled words.
  • IP Routing: Efficiently routing network packets.
  • T9 predictive text: Predicting words based on keypad presses.

This guide provides a solid foundation for understanding and implementing Tries in Python. Experiment with different applications and optimizations to further your understanding.