Implementing a Prefix Tree Data Structure

Understanding Prefix Trees

A prefix tree, commonly known as a Trie, is a specialized tree structure designed for efficient string storage and retrieval. Each node in a Trie represents a character, and the path from the root to a node represents a prefix. This structure excels in applications like autocomplete systems and spell checkers.

Initial Implementation Challenges

When first approaching this problem, one might consider a naive approach using a simple list to store strings. While functional, this method lacks efficiency:

class BasicTrie:
    def __init__(self):
        self.word_list = []

    def add_word(self, word: str) -> None:
        self.word_list.append(word)

    def contains(self, word: str) -> bool:
        return word in self.word_list

    def has_prefix(self, prefix: str) -> bool:
        for stored_word in self.word_list:
            if stored_word.startswith(prefix):
                return True
        return False

Efficient Trie Implementation

The optimal solution uses a node-based structure where each node contains an array of child references and a completion flag:

  • Children array of size 26 (for lowercase English letters)
  • Boolean flag marking the end of a complete word

Node Structure

class TrieNode:
    def __init__(self):
        self.next = [None] * 26
        self.word_complete = False

Core Operations

Word Insertion

Traverse the Trie character by character, creating nodes as needed:

def insert_word(self, word: str) -> None:
    current = self.root
    for character in word:
        index = ord(character) - 97
        if current.next[index] is None:
            current.next[index] = TrieNode()
        current = current.next[index]
    current.word_complete = True

Prefix Search

Navigate through the structure following the prefix path:

def find_prefix(self, prefix: str):
    current = self.root
    for character in prefix:
        index = ord(character) - 97
        if current.next[index] is None:
            return None
        current = current.next[index]
    return current

Complete Word Search

Verify both prefix existence and completion flag:

def word_exists(self, word: str) -> bool:
    final_node = self.find_prefix(word)
    return final_node is not None and final_node.word_complete

Prefix Verification

def prefix_exists(self, prefix: str) -> bool:
    return self.find_prefix(prefix) is not None

Complete Implementation

class Trie:
    def __init__(self):
        self.root = TrieNode()
    
    def insert(self, word: str) -> None:
        current = self.root
        for char in word:
            idx = ord(char) - 97
            if current.next[idx] is None:
                current.next[idx] = TrieNode()
            current = current.next[idx]
        current.word_complete = True
    
    def search(self, word: str) -> bool:
        result = self._traverse(word)
        return result is not None and result.word_complete
    
    def startsWith(self, prefix: str) -> bool:
        return self._traverse(prefix) is not None
    
    def _traverse(self, sequence: str):
        current = self.root
        for char in sequence:
            idx = ord(char) - 97
            if current.next[idx] is None:
                return None
            current = current.next[idx]
        return current

class TrieNode:
    def __init__(self):
        self.next = [None] * 26
        self.word_complete = False

This implementation provides O(m) time complexity for all operations, where m is the length of the input string, making it significantly more efficient than the naive approach for large datasets.

Tags: data-structures algorithms prefix-tree Trie string-operations

Posted on Thu, 29 Oct 2026 16:01:32 +0000 by jonniejoejonson