8: Tries

A trie stores strings character-by-character. Strings with common prefixes share paths. Operations are O(k)O(k) where kk is string length—independent of how many strings are stored.


Data Structure

TrieNode:
  - key: Key
  - val: Object  # non-None marks end of word
  - children: Dictionary[String, TrieNode]

Trie:
  - root: Optional[TrieNode]

Operations

Insert

insert(key, val):
    node = root
    for char in key:
        if char not in node.children:
            node.children[char] = TrieNode(char, None)
        node = node.children[char]
    node.val = val  # mark end of word
get(key):
    node = root
    for char in key:
        if char not in node.children:
            return None
        node = node.children[char]
    return node if node.val is not None else None
starts_with(prefix):
    node = root
    for char in prefix:
        if char not in node.children:
            return False
        node = node.children[char]
    return True

Autocomplete

autocomplete(prefix):
    node = root
    for char in prefix:
        if char not in node.children:
            return []
        node = node.children[char]
    return collect_all_words(node, prefix)

Runtime

OperationTime
InsertO(k)O(k)
SearchO(k)O(k)
DeleteO(k)O(k)
Prefix searchO(k)O(k)
AutocompleteO(k+m)O(k + m)

kk = string length, mm = total chars in results.

Space: O(nk)O(n \cdot k) worst case, better with shared prefixes.


Trie vs Hash Table vs BST

TrieHash TableBST
SearchO(k)O(k)O(k)O(k) avgO(klogn)O(k \log n)
Prefix searchO(k)O(k)O(nk)O(nk)complex

Tries win for prefix operations.


Key Insight

Operations depend on string length kk, not on number of strings nn. Prefix operations are trivial: walk to the prefix node, and all descendants are matches.