Tutorial Tree (Pohon) dalam Struktur Data untuk Pemula
Pendahuluan
Bayangkan kamu sedang membuka aplikasi file manager di laptopmu. Ada folder Documents, di dalamnya ada subfolder Kerja, dan di dalam Kerja ada file-file Word. Struktur bertingkat seperti ini bukan kebetulan — inilah Tree (Pohon), salah satu struktur data paling penting yang dipakai hampir di semua aplikasi modern.
Tree adalah struktur data hierarkis yang terdiri dari node-node yang saling terhubung. Berbeda dengan array atau linked list yang bersifat linear (satu baris), tree bercabang seperti pohon nyata — ada satu akar (root) di atas, dan cabang-cabang yang menjalar ke bawah.
Jika kamu sudah familiar dengan konsep dasar seperti yang dibahas di Mengenal Big O Notation: Cara Mengukur Efisiensi Algoritma, kamu akan lebih mudah memahami mengapa tree sangat efisien untuk operasi pencarian dan pengurutan data.
Konsep Dasar dan Terminologi Tree
Sebelum masuk ke kode, kita perlu memahami kosakata dasar tree:
| Istilah | Penjelasan |
|---|---|
| Root | Node paling atas, titik awal tree |
| Node | Setiap elemen dalam tree |
| Parent | Node yang memiliki cabang ke bawah |
| Child | Node yang terhubung ke parent-nya di atasnya |
| Leaf | Node yang tidak punya child (ujung cabang) |
| Edge | Garis penghubung antar node |
| Height | Jarak terpanjang dari root ke leaf |
| Depth | Jarak dari root ke sebuah node tertentu |
| Subtree | Bagian tree yang berdiri sendiri |
Visualisasi sederhana:
A ← Root (depth 0)
/ \
B C ← Child dari A (depth 1)
/ \ \
D E F ← Leaf (depth 2, height = 2)
Dalam contoh di atas:
Aadalah root dan parent dariBdanCBadalah child dariA, sekaligus parent dariDdanED,E, danFadalah leaf karena tidak punya child
Mengenal Jenis-Jenis Tree
1. General Tree
Tree yang tidak punya batasan jumlah child per node. Contoh: struktur folder di komputer.
2. Binary Tree
Setiap node memiliki maksimal 2 child — biasa disebut child kiri (left) dan child kanan (right). Ini yang paling sering dipakai dan dipelajari.
3. Binary Search Tree (BST)
Versi khusus Binary Tree dengan aturan:
- Semua nilai di kiri selalu lebih kecil dari parent
- Semua nilai di kanan selalu lebih besar dari parent
Aturan ini membuat pencarian data sangat cepat — O(log n) dalam kondisi terbaik.
4. Balanced Tree (AVL Tree, Red-Black Tree)
Tree yang menjaga keseimbangan tinggi kiri dan kanan agar operasi tetap efisien meski datanya banyak.
Untuk artikel ini, kita akan fokus pada Binary Tree dan Binary Search Tree karena paling fundamental.
Operasi Dasar dan Implementasi Sederhana
Mari kita implementasikan Binary Search Tree dari nol menggunakan Python.
Mendefinisikan Node
class Node:
def __init__(self, nilai):
self.nilai = nilai # menyimpan nilai/data pada node
self.kiri = None # anak kiri, awalnya belum ada
self.kanan = None # anak kanan, awalnya belum ada
# membuat beberapa node
akar = Node(10)
akar.kiri = Node(5)
akar.kanan = Node(15)
# menampilkan isi node
print("Nilai akar:", akar.nilai)
print("Anak kiri:", akar.kiri.nilai)
print("Anak kanan:", akar.kanan.nilai)
# Output yang diharapkan:
# > Nilai akar: 10
# > Anak kiri: 5
# > Anak kanan: 15
Mendefinisikan Binary Search Tree
class Node:
def __init__(self, nilai):
# Setiap node menyimpan satu nilai
self.nilai = nilai
# Anak kiri untuk nilai yang lebih kecil
self.kiri = None
# Anak kanan untuk nilai yang lebih besar atau sama
self.kanan = None
class BinarySearchTree:
def __init__(self):
# Awalnya tree kosong
self.root = None
def sisipkan(self, nilai):
# Jika root belum ada, buat node pertama
if self.root is None:
self.root = Node(nilai)
else:
self._sisipkan_rekursif(self.root, nilai)
def _sisipkan_rekursif(self, node, nilai):
# Jika nilai lebih kecil, masuk ke sisi kiri
if nilai < node.nilai:
if node.kiri is None:
node.kiri = Node(nilai)
else:
self._sisipkan_rekursif(node.kiri, nilai)
else:
# Jika nilai lebih besar atau sama, masuk ke sisi kanan
if node.kanan is None:
node.kanan = Node(nilai)
else:
self._sisipkan_rekursif(node.kanan, nilai)
def cari(self, nilai):
# Mulai pencarian dari root
return self._cari_rekursif(self.root, nilai)
def _cari_rekursif(self, node, nilai):
# Jika node kosong, berarti nilai tidak ditemukan
if node is None:
return False
# Jika nilai cocok, berarti ditemukan
if node.nilai == nilai:
return True
# Cari ke kiri jika nilai lebih kecil
if nilai < node.nilai:
return self._cari_rekursif(node.kiri, nilai)
# Cari ke kanan jika nilai lebih besar
return self._cari_rekursif(node.kanan, nilai)
# Contoh penggunaan
bst = BinarySearchTree()
# Menambahkan beberapa nilai ke tree
bst.sisipkan(10)
bst.sisipkan(5)
bst.sisipkan(15)
bst.sisipkan(3)
bst.sisipkan(7)
# Mencari nilai dalam tree
print("Cari 7:", bst.cari(7))
print("Cari 12:", bst.cari(12))
# Output yang diharapkan:
# > Cari 7: True
# > Cari 12: False
Tree Traversal — Cara Mengunjungi Semua Node
Ada tiga cara traversal utama:
class Node:
def __init__(self, nilai):
self.nilai = nilai
self.kiri = None
self.kanan = None
class BinaryTree:
# In-order: kiri -> root -> kanan
def in_order(self, node, hasil=None):
if hasil is None:
hasil = [] # Membuat list baru agar tidak berbagi data antar pemanggilan
if node:
self.in_order(node.kiri, hasil) # Kunjungi subtree kiri
hasil.append(node.nilai) # Simpan nilai node saat ini
self.in_order(node.kanan, hasil) # Kunjungi subtree kanan
return hasil
# Pre-order: root -> kiri -> kanan
def pre_order(self, node, hasil=None):
if hasil is None:
hasil = []
if node:
hasil.append(node.nilai) # Simpan root lebih dulu
self.pre_order(node.kiri, hasil) # Lalu subtree kiri
self.pre_order(node.kanan, hasil) # Terakhir subtree kanan
return hasil
# Post-order: kiri -> kanan -> root
def post_order(self, node, hasil=None):
if hasil is None:
hasil = []
if node:
self.post_order(node.kiri, hasil) # Kunjungi subtree kiri
self.post_order(node.kanan, hasil) # Kunjungi subtree kanan
hasil.append(node.nilai) # Simpan root di akhir
return hasil
# Membuat tree sederhana
# 2
# / \
# 1 3
root = Node(2)
root.kiri = Node(1)
root.kanan = Node(3)
tree = BinaryTree()
# Menampilkan hasil traversal
print("In-order :", tree.in_order(root))
print("Pre-order:", tree.pre_order(root))
print("Post-order:", tree.post_order(root))
# Output yang diharapkan:
# > In-order : [1, 2, 3]
# > Pre-order: [2, 1, 3]
# > Post-order: [1, 3, 2]
Contoh Penggunaan Lengkap
class Node:
def __init__(self, data):
self.data = data
self.kiri = None
self.kanan = None
class BinarySearchTree:
def __init__(self):
self.root = None
def sisipkan(self, data):
# Jika tree masih kosong, data pertama jadi root
if self.root is None:
self.root = Node(data)
else:
self._sisipkan_rekursif(self.root, data)
def _sisipkan_rekursif(self, node, data):
# Nilai lebih kecil masuk ke kiri
if data < node.data:
if node.kiri is None:
node.kiri = Node(data)
else:
self._sisipkan_rekursif(node.kiri, data)
# Nilai lebih besar atau sama masuk ke kanan
else:
if node.kanan is None:
node.kanan = Node(data)
else:
self._sisipkan_rekursif(node.kanan, data)
def in_order(self, node, hasil):
# Kiri -> Root -> Kanan
if node is not None:
self.in_order(node.kiri, hasil)
hasil.append(node.data)
self.in_order(node.kanan, hasil)
return hasil
def pre_order(self, node, hasil):
# Root -> Kiri -> Kanan
if node is not None:
hasil.append(node.data)
self.pre_order(node.kiri, hasil)
self.pre_order(node.kanan, hasil)
return hasil
def cari(self, data):
# Mulai pencarian dari root
return self._cari_rekursif(self.root, data)
def _cari_rekursif(self, node, data):
# Jika node tidak ada, data tidak ditemukan
if node is None:
return False
# Jika data sama, berarti ditemukan
if node.data == data:
return True
# Cari ke kiri jika data lebih kecil
if data < node.data:
return self._cari_rekursif(node.kiri, data)
# Cari ke kanan jika data lebih besar
return self._cari_rekursif(node.kanan, data)
# Buat tree dan isi data
bst = BinarySearchTree()
for angka in [50, 30, 70, 20, 40, 60, 80]:
bst.sisipkan(angka)
# Struktur yang terbentuk:
# 50
# / \
# 30 70
# / \ / \
# 20 40 60 80
# Traversal in-order menghasilkan data terurut
print("In-order:", bst.in_order(bst.root, []))
# Traversal pre-order membaca root lebih dulu
print("Pre-order:", bst.pre_order(bst.root, []))
# Pencarian data di dalam tree
print("Cari 40:", bst.cari(40))
print("Cari 99:", bst.cari(99))
# Output yang diharapkan:
# > In-order: [20, 30, 40, 50, 60, 70, 80]
# > Pre-order: [50, 30, 20, 40, 70, 60, 80]
# > Cari 40: True
# > Cari 99: False
Perhatikan hasil in-order traversal yang menghasilkan data terurut secara otomatis. Inilah kekuatan BST — data selalu dalam kondisi “siap sort” tanpa perlu algoritma sorting tambahan.
Contoh Kasus Nyata
1. Sistem Navigasi Kategori Produk
Bayangkan kamu ingin membangun e-commerce seperti Tokopedia atau Shopee. Kategori produk mereka berbentuk tree:
Elektronik
├── Smartphone
│ ├── Android
│ └── iOS
├── Laptop
│ ├── Gaming
│ └── Ultrabook
└── Aksesoris
├── Charger
└── Headset
class KategoriProduk:
def __init__(self, nama):
# Menyimpan nama kategori
self.nama = nama
# Menyimpan daftar sub-kategori
self.sub_kategoris = []
def tambah_sub_kategori(self, sub_kategori):
self.sub_kategoris.append(sub_kategori)
def cetak_kategoris(self, level=0):
print(' ' * level + self.nama)
for sub_kategori in self.sub_kategoris:
sub_kategori.cetak_kategoris(level + 1)
# Membuat struktur kategori
elektronik = KategoriProduk("Elektronik")
smartphone = KategoriProduk("Smartphone")
android = KategoriProduk("Android")
ios = KategoriProduk("iOS")
laptop = KategoriProduk("Laptop")
gaming = KategoriProduk("Gaming")
ultrabook = KategoriProduk("Ultrabook")
aksesoris = KategoriProduk("Aksesoris")
charger = KategoriProduk("Charger")
headset = KategoriProduk("Headset")
smartphone.tambah_sub_kategori(android)
smartphone.tambah_sub_kategori(ios)
laptop.tambah_sub_kategori(gaming)
laptop.tambah_sub_kategori(ultrabook)
aksesoris.tambah_sub_kategori(charger)
aksesoris.tambah_sub_kategori(headset)
elektronik.tambah_sub_kategori(smartphone)
elektronik.tambah_sub_kategori(laptop)
elektronik.tambah_sub_kategori(aksesoris)
elektronik.cetak_kategoris()
# Output yang diharapkan:
# > Elektronik
# > Smartphone
# > Android
# > iOS
# > Laptop
# > Gaming
# > Ultrabook
# > Aksesoris
# > Charger
# > Headset
2. Autocomplete dengan Trie
Trie adalah bentuk pohon yang sering digunakan untuk implementasi autocomplete, seperti fitur search suggestion yang kamu temukan di mesin pencari atau aplikasi chat.
class TrieNode:
def __init__(self):
self.children = {}
self.is_end_of_word = 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_of_word = True
def search_prefix(self, prefix):
node = self.root
for char in prefix:
if char not in node.children:
return None
node = node.children[char]
return node
def autocomplete(self, prefix):
results = []
prefix_node = self.search_prefix(prefix)
if not prefix_node:
return results
self._find_words_from_node(prefix_node, prefix, results)
return results
def _find_words_from_node(self, node, prefix, results):
if node.is_end_of_word:
results.append(prefix)
for char, child in node.children.items():
self._find_words_from_node(child, prefix + char, results)
# Contoh penggunaan
trie = Trie()
words = ["apple", "app", "apricot", "banana", "bandana"]
for word in words:
trie.insert(word)
print(trie.autocomplete("app")) # Output: ['app', 'apple']
print(trie.autocomplete("ban")) # Output: ['banana', 'bandana']
print(trie.autocomplete("xyz")) # Output: []
Pertanyaan yang Sering Diajukan
Apa perbedaan Tree dan Graph dalam struktur data?
Tree adalah jenis khusus dari Graph dengan aturan ketat: tidak ada siklus (cycle), setiap node hanya punya satu parent (kecuali root), dan selalu ada tepat satu jalur antara dua node. Graph lebih bebas — bisa punya siklus dan satu node bisa terhubung ke banyak node lain tanpa hierarki yang jelas. Singkatnya, semua tree adalah graph, tapi tidak semua graph adalah tree.
Mengapa Binary Search Tree lebih efisien dari pencarian linear?
Pada pencarian linear (seperti di array), kita harus memeriksa setiap elemen satu per satu — kompleksitasnya O(n). Pada BST yang seimbang, setiap perbandingan memotong separuh dari kemungkinan — kompleksitasnya O(log n). Untuk 1 juta data, linear butuh hingga 1.000.000 langkah, sedangkan BST hanya butuh sekitar 20 langkah.
Bagaimana cara memilih jenis traversal yang tepat?
Pilih berdasarkan kebutuhanmu: gunakan in-order jika kamu ingin data terurut dari kecil ke besar (cocok untuk sorting). Gunakan pre-order jika kamu ingin menyalin atau menyerialkan tree (root diproses duluan). Gunakan post-order jika kamu ingin menghapus tree dari bawah ke atas, atau menghitung nilai ekspresi matematika bertingkat.
Apa itu Balanced Tree dan kenapa penting?
Balanced tree adalah tree yang menjaga agar tinggi kiri dan kanan tidak terlalu berbeda. Jika BST tidak seimbang — misalnya semua data dimasukkan secara urut (1, 2, 3, 4, 5) — maka tree akan menjadi seperti linked list dengan tinggi O(n), dan semua keuntungan BST hilang. Balanced tree seperti AVL Tree atau Red-Black Tree otomatis menyeimbangkan diri setelah setiap operasi.
Kapan sebaiknya saya menggunakan Tree dibanding Hash Table?
Gunakan Tree jika kamu perlu data yang terurut, pencarian range (misalnya: cari semua nilai antara 10 dan 50), atau operasi in-order traversal. Untuk perbandingan lengkap keduanya, baca Apa itu Hash Table: Penjelasan Lengkap untuk Pemula. Hash Table lebih cepat untuk pencarian tepat (exact lookup) dengan O(1) rata-rata, tapi tidak mendukung pengurutan. Keduanya punya keunggulan masing-masing tergantung kasusnya.
Kesimpulan
Tree adalah struktur data hierarkis yang sangat powerful untuk merepresentasikan data bertingkat. Poin penting yang perlu kamu ingat:
- Root adalah satu-satunya node tanpa parent; leaf adalah node tanpa child
- Binary Search Tree (BST) mengatur data agar kiri < parent < kanan, membuat pencarian efisien O(log n)
- In-order traversal menghasilkan data terurut secara otomatis
- Aplikasi nyata: sistem file, kategori produk, autocomplete, ekspresi matematika, dan masih banyak lagi
Tree mungkin terlihat kompleks di awal, tapi begitu kamu memahami prinsip rekursinya — setiap subtree adalah tree itu sendiri — semuanya akan terasa lebih natural. Mulailah dengan mengimplementasikan BST sederhana, lalu eksplorasi varian lainnya seperti AVL Tree atau Heap.
Selamat belajar, dan terus berlatih!