Vasu Patel logoVasu Patel
← Python DSA — Beginner
All chapters in this track
  1. 011. What is DSA & Why It Matters
  2. 022. Python's Built-in Collections
  3. 033. Arrays & Strings
  4. 044. Linked Lists
  5. 055. Stacks & Queues
  6. 066. Hash Maps & Sets
  7. 077. Trees
  8. 088. Heaps & Priority Queues
  9. 099. Graphs
  10. 1010. Sorting & Searching
  11. 1111. Recursion
  12. 1212. Dynamic Programming
  13. 1313. Tries
  14. 1414. DSA in Real Software

Chapter 13 of 14

13. Tries

What Is a Trie?

A Trie (pronounced "try", from retrieval) is a tree structure where each path from root to a node spells out a string. Every character of a word is stored as a separate node.

Analogy: A dictionary organised by letter-by-letter branching. To look up "cat", go to the 'c' branch, then 'a', then 't'.

Real-world uses:

  • Autocomplete — type "se" and get "search", "settings", "send"
  • Spell checkers — verify word exists or find closest match
  • IP routing tables — match longest prefix of an IP address
  • Search engines — suggest queries as you type

13.1 Trie Implementation

class TrieNode:
    def __init__(self):
        self.children: dict[str, "TrieNode"] = {}
        self.is_end_of_word: bool = False

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

    def insert(self, word: str) -> None:
        """Add a word to the trie — O(L) where L = word length."""
        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: str) -> bool:
        """Return True if the exact word exists — O(L)."""
        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: str) -> bool:
        """Return True if any word starts with this prefix — O(L)."""
        node = self.root
        for char in prefix:
            if char not in node.children:
                return False
            node = node.children[char]
        return True

    def autocomplete(self, prefix: str) -> list[str]:
        """Return all words that start with prefix."""
        node = self.root
        for char in prefix:
            if char not in node.children:
                return []
            node = node.children[char]
        results = []
        self._dfs(node, prefix, results)
        return results

    def _dfs(self, node: TrieNode, current: str, results: list[str]) -> None:
        if node.is_end_of_word:
            results.append(current)
        for char, child in node.children.items():
            self._dfs(child, current + char, results)


# Usage
trie = Trie()
for word in ["apple", "app", "application", "apply", "banana", "band"]:
    trie.insert(word)

print(trie.search("app"))          # True
print(trie.search("ap"))           # False (not a full word)
print(trie.starts_with("ap"))      # True
print(trie.autocomplete("app"))    # ['app', 'apple', 'application', 'apply']
print(trie.autocomplete("ban"))    # ['banana', 'band']

13.2 When to Use a Trie vs a Hash Set

ScenarioBest Tool
Check if exact word existsset — simpler, O(1)O(1)
Check if prefix existsTrie — set can't do this efficiently
Autocomplete suggestionsTrie
Count words with prefixTrie (store counts at nodes)
All words matching wildcard patternTrie with DFS