Trie menyimpan string dengan shared prefix untuk efisiensi ruang. Di episode ini kalian memahami operasi insert, search, startsWith (autocomplete), dan delete, serta membandingkan trie dengan hash set untuk prefix query dan mempraktikkan implementasi autocomplete sederhana.

Setelah di episode 13 kita memahami B-Tree dan B+ Tree — tree dengan branching factor tinggi untuk database index — pada episode ini kita mempelajari Trie (prefix tree), tree di mana setiap node merepresentasikan satu karakter dan string yang memiliki prefix sama akan berbagi node yang sama.
Trie bukan hanya konsep teori — ia adalah implementasi di balik autocomplete yang kalian gunakan setiap hari di search engine, chat apps, dan IDE. Ketika kalian mengetik "dev" dan melihat saran "developer", "device", "devvnull" — itulah trie yang bekerja di balik layar.
Trie adalah tree di mana:
Ketika banyak string memiliki prefix yang sama (misal: "dev", "developer", "device"), trie menyimpan prefix "dev" hanya sekali. Ini menghemat ruang dibandingkan menyimpan setiap string secara terpisah di hash set.
class TrieNode:
def __init__(self):
self.children = {}
self.is_end = False
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, word):
node = self.root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
node.is_end = Truedef search(self, word):
node = self.root
for char in word:
if char not in node.children:
return False
node = node.children[char]
return node.is_enddef starts_with(self, prefix):
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):
node = self.root
for char in prefix:
if char not in node.children:
return []
node = node.children[char]
results = []
self._collect_words(node, prefix, results)
return results
def _collect_words(self, node, prefix, results):
if node.is_end:
results.append(prefix)
for char, child in node.children.items():
self._collect_words(child, prefix + char, results)def delete(self, word):
def _delete(node, word, depth):
if depth == len(word):
if not node.is_end:
return False
node.is_end = False
return len(node.children) == 0
char = word[depth]
if char not in node.children:
return False
should_delete = _delete(node.children[char], word, depth + 1)
if should_delete:
del node.children[char]
return len(node.children) == 0 and not node.is_end
return False
_delete(self.root, word, 0)| Operasi | Trie | Hash Set |
|---|---|---|
| Insert | O(m) | O(m) |
| Search exact | O(m) | O(m) |
| Prefix query | O(m + k) | O(n × m) |
| Space | Shared prefix | No sharing |
m = panjang string, k = jumlah hasil, n = jumlah string. Trie menang untuk prefix query karena prefix di-share.
trie = Trie()
words = ["developer", "device", "devvnull", "design", "desktop"]
for word in words:
trie.insert(word)
print(trie.autocomplete("dev"))
print(trie.autocomplete("des"))Tip
Untuk production autocomplete, trie bisa dikompresi menjadi radix tree (compressed trie) di mana node dengan satu child digabungkan. Ini mengurangi jumlah node dan mempercepat traversal. MongoDB menggunakan radix tree untuk optimasi prefix index.
Inti yang harus dibawa pulang:
Ini adalah episode terakhir di Fase 3: Trees. Di episode 15 selanjutnya kita memasuki Fase 4: Graphs dengan membahas representasi graph — directed/undirected, weighted/unweighted, adjacency list vs adjacency matrix, serta trade-off memory vs lookup edge. Trees pada dasarnya adalah graph tanpa cycle — jadi pemahaman kalian sudah siap untuk lompatan ke graph!