Course contentsShow
← C#
Lesson 341 of 1,9083. Generics and Collections ArchitectureFree lesson

Trie and Prefix Tree Implementations

Architect memory-efficient prefix trees for fast lookup of string-based keys.

In this lesson, you will learn how to architect memory-efficient Tries to optimize prefix-based string lookups in C#.

The Core Concept

A Trie, or prefix tree, is an n-ary tree structure used to store a dynamic set of strings. Unlike a Dictionary<string, T>, which hashes the entire key, a Trie stores keys by breaking them into individual characters across tree nodes. Each node represents a prefix, and by following a path from the root, you can determine if a word exists in the set without ever hashing the full string.

This approach is highly memory-efficient for large datasets with many common prefixes, as common character sequences are stored only once.

Architectural Implementation

To build a performant Trie, we define a node structure that holds a collection of children and a flag indicating if the current path represents a complete word. Using a Dictionary<char, TrieNode> within each node allows for O(1)O(1) transitions per character:

public class TrieNode
{
    // Dictionary ensures fast lookup for the next character
    public Dictionary<char, TrieNode> Children { get; } = new();
    public bool IsEndOfWord { get; set; }
}

public class PrefixTree
{
    private readonly TrieNode _root = new();

    public void Insert(string word)
    {
        var current = _root;
        foreach (var ch in word)
        {
            if (!current.Children.TryGetValue(ch, out var next))
            {
                next = new TrieNode();
                current.Children[ch] = next;
            }
            current = next;
        }
        current.IsEndOfWord = true;
    }
}

Efficiency Considerations

While Dictionary<char, TrieNode> provides excellent performance, it adds overhead per node due to the dictionary object itself. If your memory constraints are extreme and your character set is small (like A-Z), you might replace the Dictionary with a fixed-size array (TrieNode[26]).

However, for general-purpose applications, the dictionary approach strikes a balance between memory usage and lookup speed. Because we navigate the tree based on the length of the string (mm), our search time complexity becomes O(m)O(m), which is independent of the total number of words in your collection—making it significantly faster than standard collections for prefix-heavy operations.

Key Takeaway: Tries optimize prefix lookups by sharing common character nodes, effectively trading memory for a performance boost that ignores the total number of items in the collection.