Tree (pohon) adalah salah satu struktur data non-linear yang paling penting dalam ilmu komputer. Berbeda dengan struktur data linear seperti array atau linked list, tree mengorganisir data dalam bentuk hierarkis yang menyerupai pohon terbalik. Struktur ini sangat efisien untuk operasi pencarian, penyisipan, dan penghapusan data, terutama ketika diimplementasikan dalam bentuk Binary Search Tree (BST) dan AVL Tree.
Dalam artikel ini, kita akan mempelajari secara mendalam tentang konsep tree dasar, berbagai jenis binary tree, implementasi Binary Search Tree, hingga optimasi dengan AVL Tree yang menjaga keseimbangan tinggi pohon secara otomatis.
📑 Daftar Isi
1. Pengertian Tree (Pohon)
Definisi Tree
Tree adalah kumpulan node yang dapat berupa:
- Empty (kosong) - tidak memiliki node sama sekali
- Node Root (r) dengan nol atau lebih subtree T₁, T₂, ..., Tₖ, dimana setiap root subtree terhubung dengan edge (garis) ke r
Secara matematis, sebuah tree dengan n node memiliki tepat (n-1) edge.
Tree adalah struktur data hierarkis yang terdiri dari node-node yang saling terhubung. Struktur ini sangat berguna dalam berbagai aplikasi seperti sistem file komputer, database indexing, representasi ekspresi matematika, dan algoritma pencarian.
💡 Karakteristik Utama Tree
- Memiliki satu root node yang unik (tidak memiliki predecessor)
- Setiap node kecuali root memiliki tepat satu parent
- Tidak memiliki cycle (siklus) - tidak ada jalur yang kembali ke node asal
- Terdapat jalur unik dari root ke setiap node
2. Terminologi dan Konsep Dasar Tree
Untuk memahami tree dengan baik, kita perlu mengenal berbagai istilah yang digunakan:
🔝 Root (Akar)
Node paling atas yang tidak memiliki predecessor. Root adalah titik awal dari seluruh struktur tree.
🍃 Leaf (Daun)
Node yang tidak memiliki successor (children). Leaf adalah node terminal di ujung tree.
👨👦 Parent-Child
Parent adalah predecessor langsung dari suatu node. Child adalah successor langsung dari parent.
👫 Sibling
Node-node yang memiliki parent yang sama. Mereka adalah saudara dalam hierarki tree.
⬆️ Ancestors
Semua node dalam jalur dari suatu node ke root, termasuk parent, grandparent, dan seterusnya.
⬇️ Descendants
Semua node yang dapat dicapai dari suatu node, termasuk children, grandchildren, dan seterusnya.
Konsep Path, Depth, dan Height
📏 Path (Jalur)
Path dari node n₁ ke nₖ adalah urutan node n₁, n₂, ..., nₖ dimana nᵢ adalah parent dari nᵢ₊₁. Panjang path didefinisikan sebagai jumlah edge pada jalur tersebut.
🔢 Depth (Kedalaman)
Depth dari node nᵢ adalah panjang path dari root ke nᵢ. Depth root selalu 0.
📊 Height (Tinggi)
Height dari node nᵢ adalah panjang path terpanjang dari nᵢ ke leaf. Height root sama dengan height tree secara keseluruhan.
📌 Contoh Ilustrasi
Misalkan kita memiliki tree dengan struktur:
- Root: A
- Children dari A: B, C, D, E, F, G
- Children dari E: I, J
- Children dari J: P, Q
- Leaves: B, C, H, I, K, L, M, N, P, Q
Dalam contoh ini:
- Ancestors dari J adalah: E dan A
- Descendants dari E adalah: I, J, P, dan Q
- Sibling dari F adalah: B, C, D, E, dan G
3. Implementasi Tree dengan Linked List
Tree dapat diimplementasikan menggunakan linked list dengan struktur khusus. Karena jumlah children pada setiap node dapat bervariasi dan tidak diketahui sebelumnya, kita menggunakan pendekatan First Child - Next Sibling.
Struktur Data Tree Node
struct tree_node {
datatype data;
tree_node *FC; // First Child - pointer ke child pertama
tree_node *NS; // Next Sibling - pointer ke sibling berikutnya
};
💡 Konsep First Child - Next Sibling
Teknik ini mengatasi masalah variasi jumlah children dengan cara:
- FC (First Child): Menunjuk ke anak pertama dari node
- NS (Next Sibling): Menunjuk ke saudara berikutnya yang memiliki parent sama
Dengan cara ini, setiap node hanya membutuhkan dua pointer, namun dapat merepresentasikan tree dengan jumlah children tidak terbatas.
Contoh Implementasi untuk File System
struct node {
string name; // nama file atau directory
long size; // ukuran file
node *FC; // First Child
node *NS; // Next Sibling
};
Struktur ini sangat cocok untuk merepresentasikan sistem file komputer, dimana setiap directory dapat memiliki banyak file dan subdirectory.
4. Traversal Tree
Traversal adalah proses mengunjungi setiap node dalam tree secara sistematis. Ada tiga jenis traversal utama yang analog dengan notasi ekspresi matematika:
1️⃣ PreOrder
Mengunjungi node sebelum mengunjungi subtree-nya (analog dengan notasi Prefix)
Urutan: Root → Left → Right
2️⃣ InOrder
Mengunjungi node di antara subtree kiri dan kanan (analog dengan notasi Infix)
Urutan: Left → Root → Right
3️⃣ PostOrder
Mengunjungi node setelah semua subtree-nya dikunjungi (analog dengan notasi Postfix)
Urutan: Left → Right → Root
Implementasi Traversal
PreOrder Traversal - List Directory
void ListDir(node *n, int depth) {
if (n) {
print(n, depth); // Cetak node saat ini
ListDir(n->FC, depth+1); // Kunjungi children
ListDir(n->NS, depth); // Kunjungi sibling
}
}
PostOrder Traversal - Calculate Size
long Trace(node *n, int depth) {
long t_size = 0;
if (n) {
t_size = n->size; // Ambil size node
t_size += Trace(n->FC, depth+1); // Hitung size children
print(n, depth, t_size); // Cetak setelah children
t_size += Trace(n->NS, depth); // Tambah size sibling
}
return t_size;
}
🎯 Aplikasi Traversal
- PreOrder: Membuat salinan tree, menampilkan struktur directory
- InOrder: Mendapatkan data terurut dari BST
- PostOrder: Menghapus tree, menghitung ukuran directory
5. Binary Tree (Pohon Biner)
Definisi Binary Tree
Binary Tree adalah tree dimana setiap node maksimal memiliki dua children (anak). Kedua anak ini biasanya disebut sebagai left child (anak kiri) dan right child (anak kanan).
Binary Tree adalah salah satu struktur data tree yang paling sering digunakan karena kesederhanaannya dan efisiensi operasinya. Struktur ini menjadi dasar untuk berbagai struktur data lanjutan seperti BST, AVL Tree, Red-Black Tree, dan Heap.
Jenis-Jenis Binary Tree
1. Complete Binary Tree (Pohon Biner Lengkap)
Complete Binary Tree adalah binary tree dimana:
- Semua level terisi penuh kecuali mungkin level terakhir
- Level terakhir diisi dari kiri ke kanan
- Setiap node memiliki 0 atau 2 children (tidak ada node dengan 1 child)
- Setiap subtree dapat memiliki panjang path yang berbeda
Karakteristik: Optimal untuk implementasi Heap
2. Full Binary Tree (Pohon Biner Penuh)
Full Binary Tree memiliki properti yang lebih ketat:
- Sama seperti Complete Binary Tree
- Semua subtree harus memiliki panjang path yang sama
- Semua leaf berada pada level yang sama
- Setiap internal node memiliki tepat 2 children
Karakteristik: Bentuk tree yang paling seimbang sempurna
3. Skewed Binary Tree (Pohon Biner Miring)
Skewed Binary Tree adalah bentuk tree yang tidak seimbang:
- Sebagian besar node hanya memiliki satu child
- Bentuknya menyerupai linked list
- Sangat tidak efisien untuk operasi pencarian
- Kompleksitas operasi menjadi O(n) seperti linked list
⚠️ Performa Buruk: Harus dihindari dengan balancing
Struktur Data Binary Tree
struct BinaryNode {
datatype data;
BinaryNode *left; // Pointer ke left child
BinaryNode *right; // Pointer ke right child
};
Implementasi Array untuk Binary Tree
💡 Formula Indexing Binary Tree dalam Array
Binary Tree dapat direpresentasikan dalam array dengan formula:
- Root: Index 0
- Left child dari index p: 2p + 1
- Right child dari index p: 2p + 2
- Parent dari index p: (p - 1) / 2
Metode ini sangat efisien untuk Complete Binary Tree karena tidak ada waste space.
6. Expression Tree
Expression Tree adalah aplikasi khusus dari Binary Tree yang merepresentasikan ekspresi matematika. Struktur ini sangat berguna untuk:
- Parsing dan evaluasi ekspresi matematika
- Compiler dan interpreter untuk bahasa pemrograman
- Kalkulator scientific
- Symbolic mathematics
Karakteristik Expression Tree
🔢 Operand (Leaf Node)
Angka atau variabel berada di leaf nodes
➕ Operator (Internal Node)
Operator matematika (+, -, *, /) berada di internal nodes
Cara Membuat Expression Tree
Langkah-langkah:
- Konversi ekspresi infix ke postfix (Reverse Polish Notation)
- Buat stack untuk menyimpan pointer ke node tree
- Scan ekspresi postfix dari kiri ke kanan:
- Jika operand: buat node baru dan push ke stack
- Jika operator: pop 2 node dari stack, buat node operator dengan kedua node sebagai children, push kembali ke stack
- Node terakhir di stack adalah root dari expression tree
📌 Contoh: (a + b * c) + ((d * e + f) * g)
Langkah 1: Konversi ke postfix
a b c * + d e * f + g * +
Langkah 2: Konstruksi tree dari postfix
Root tree akan berisi operator '+' terakhir, dengan subtree kiri 'a + b * c' dan subtree kanan '(d * e + f) * g'
Traversal pada Expression Tree
| Jenis Traversal | Hasil | Notasi |
|---|---|---|
| PreOrder | + + a * b c * + * d e f g | Prefix (Polish Notation) |
| InOrder | a + b * c + d * e + f * g | Infix (perlu tambahan tanda kurung) |
| PostOrder | a b c * + d e * f + g * + | Postfix (Reverse Polish Notation) |
7. Binary Search Tree (BST)
Definisi Binary Search Tree
Binary Search Tree (BST) adalah Binary Tree dengan properti khusus untuk setiap node X:
- Semua nilai node di left subtree harus lebih kecil dari nilai X
- Semua nilai node di right subtree harus lebih besar dari nilai X
Properti ini berlaku secara rekursif untuk semua node dalam tree.
Keunggulan BST
🚀 Pencarian Cepat
Kompleksitas O(log n) untuk tree seimbang, jauh lebih cepat dibanding array O(n)
📥 Insertion Efisien
Tidak perlu shifting elemen seperti pada array terurut
🗑️ Deletion Fleksibel
Dapat menghapus node dengan berbagai kasus tanpa reorganisasi besar
📊 Data Terurut
InOrder traversal menghasilkan data dalam urutan terurut
Struktur Data BST
struct SNode {
int data;
SNode *left;
SNode *right;
};
SNode *Root; // Pointer ke root tree
Operasi Insert pada BST
Algoritma Insert
- Jika tree kosong, buat node baru sebagai root
- Jika data lebih kecil dari node saat ini, insert ke left subtree
- Jika data lebih besar dari node saat ini, insert ke right subtree
- Jika data sama (opsional: tambahkan counter atau tolak)
SNode* Insert(int dat, SNode* T) {
if (!T) {
// Tree kosong, buat node baru
SNode* temp = new SNode;
temp->data = dat;
temp->left = nullptr;
temp->right = nullptr;
return temp;
} else {
if (dat < T->data)
T->left = Insert(dat, T->left); // Insert ke kiri
else if (dat > T->data)
T->right = Insert(dat, T->right); // Insert ke kanan
return T;
}
}
💡 Handling Duplikasi
Untuk mengizinkan data duplikat, kita dapat menambahkan field counter:
struct SNode {
int data;
int occur; // Jumlah kemunculan data
SNode *left;
SNode *right;
};
Operasi Delete pada BST
Operasi delete lebih kompleks karena ada tiga kasus yang harus ditangani:
Kasus 1: Leaf Node
Node tidak memiliki children. Cukup hapus node tersebut.
Kasus 2: Satu Child
Node memiliki satu child. Ganti node dengan child-nya.
Kasus 3: Dua Children
Node memiliki dua children. Ganti dengan inorder successor (minimum dari right subtree) atau inorder predecessor (maximum dari left subtree).
SNode* Delete(int dat, SNode* n) {
SNode* cur;
if (n) {
if (dat < n->data)
n->left = Delete(dat, n->left);
else if (dat > n->data)
n->right = Delete(dat, n->right);
else {
// Node ditemukan
if (n->left && n->right) {
// Kasus 3: dua children
// Cari minimum dari right subtree
FindMin(n);
} else if (n->left) {
// Kasus 2: hanya left child
cur = n;
n = n->left;
delete cur;
} else {
// Kasus 2: hanya right child atau leaf
cur = n;
n = n->right;
delete cur;
}
}
return n;
} else
return nullptr;
}
Kompleksitas BST
| Operasi | Best Case | Average Case | Worst Case |
|---|---|---|---|
| Search | O(log n) | O(log n) | O(n) |
| Insert | O(log n) | O(log n) | O(n) |
| Delete | O(log n) | O(log n) | O(n) |
⚠️ Kelemahan BST
Worst case O(n) terjadi ketika BST menjadi skewed (tidak seimbang). Contoh: insert data terurut (1, 2, 3, 4, 5) akan menghasilkan tree yang menyerupai linked list.
Solusi: Gunakan Self-Balancing BST seperti AVL Tree atau Red-Black Tree!
8. AVL Tree - Height Balanced Tree
Definisi AVL Tree
AVL Tree adalah Binary Search Tree yang menjaga properti height balanced, yaitu perbedaan tinggi antara left subtree dan right subtree untuk setiap node maksimal 1.
Nama AVL berasal dari penemunya: Adelson-Velsky dan Landis (1962).
AVL Tree adalah solusi untuk mengatasi kelemahan BST yang dapat menjadi tidak seimbang. Dengan menjaga keseimbangan tinggi tree, AVL Tree memastikan bahwa semua operasi tetap memiliki kompleksitas O(log n).
Konsep Balance Factor (BF)
Balance Factor
Balance Factor dari sebuah node adalah selisih tinggi left subtree dan right subtree:
BF(node) = height(left subtree) - height(right subtree)
Dalam AVL Tree yang valid:
- BF setiap node harus bernilai -1, 0, atau +1
- BF = -1: Right subtree lebih tinggi 1 level
- BF = 0: Left dan right subtree seimbang
- BF = +1: Left subtree lebih tinggi 1 level
Properti AVL Tree
🎯 Height Balanced
|hL - hR| ≤ 1 untuk setiap node, dimana hL dan hR adalah tinggi left dan right subtree
📏 Tinggi Optimal
Height AVL Tree dengan n node adalah O(log n), menjamin operasi cepat
♻️ Self-Balancing
Otomatis melakukan rotasi saat terjadi ketidakseimbangan setelah insert atau delete
🔍 Efisien
Semua operasi (search, insert, delete) dijamin O(log n)
Struktur Data AVL Tree
struct Node {
int data;
int height; // Menyimpan tinggi node untuk efisiensi
Node *left;
Node *right;
};
Node *Root;
Fungsi Bantuan
// Mendapatkan tinggi node
int Height(Node* n) {
return (n) ? n->height : -1;
}
// Mendapatkan maksimum dari dua nilai
int Max(int L, int R) {
return (L > R) ? L : R;
}
// Menghitung Balance Factor
int BalanceFactor(Node* n) {
if (!n) return 0;
return Height(n->left) - Height(n->right);
}
Jenis Rotasi pada AVL Tree
Ketika terjadi ketidakseimbangan (BF = -2 atau +2), AVL Tree melakukan rotasi untuk mengembalikan keseimbangan. Ada 4 jenis rotasi:
1. Single Right Rotation (LL Case)
Digunakan ketika:
- Node memiliki BF = +2 (left subtree lebih tinggi)
- Left child memiliki BF = +1 atau 0
void Right_Rotation(Node* k2) {
Node* k1;
k1 = k2->left;
k2->left = k1->right;
k1->right = k2;
// Update heights
k2->height = Max(Height(k2->left), Height(k2->right)) + 1;
k1->height = Max(Height(k1->left), k2->height) + 1;
k2 = k1; // k1 menjadi root baru
}
2. Single Left Rotation (RR Case)
Digunakan ketika:
- Node memiliki BF = -2 (right subtree lebih tinggi)
- Right child memiliki BF = -1 atau 0
void Left_Rotation(Node* k1) {
Node* k2;
k2 = k1->right;
k1->right = k2->left;
k2->left = k1;
// Update heights
k1->height = Max(Height(k1->left), Height(k1->right)) + 1;
k2->height = Max(Height(k2->right), k1->height) + 1;
k1 = k2; // k2 menjadi root baru
}
3. Left-Right Rotation (LR Case)
Digunakan ketika:
- Node memiliki BF = +2
- Left child memiliki BF = -1
Rotasi ganda: Left rotation pada left child, kemudian Right rotation pada node
void Left_Right(Node* k3) {
Left_Rotation(k3->left); // Rotasi kiri pada k1
Right_Rotation(k3); // Rotasi kanan pada k3
}
4. Right-Left Rotation (RL Case)
Digunakan ketika:
- Node memiliki BF = -2
- Right child memiliki BF = +1
Rotasi ganda: Right rotation pada right child, kemudian Left rotation pada node
void Right_Left(Node* k1) {
Right_Rotation(k1->right); // Rotasi kanan pada k3
Left_Rotation(k1); // Rotasi kiri pada k1
}
📌 Kapan Menggunakan Rotasi?
| Kasus | BF Node | BF Child | Rotasi |
|---|---|---|---|
| LL | +2 | +1 atau 0 | Right Rotation |
| RR | -2 | -1 atau 0 | Left Rotation |
| LR | +2 | -1 | Left-Right Rotation |
| RL | -2 | +1 | Right-Left Rotation |
9. Operasi pada AVL Tree
Operasi Insert pada AVL Tree
Algoritma Insert AVL
- Lakukan insert seperti BST biasa
- Update height dari node yang di-insert ke atas
- Cek Balance Factor setiap node dalam path
- Jika BF = -2 atau +2, lakukan rotasi yang sesuai
Node* Insertion(int x, Node* p) {
if (!p) {
// Buat node baru
p = new Node;
p->data = x;
p->height = 0;
p->left = p->right = nullptr;
} else {
if (x < p->data) {
p->left = Insertion(x, p->left);
// Cek balance factor
if (abs(Height(p->left) - Height(p->right)) == 2) {
if (x < p->left->data)
Right_Rotation(p); // LL case
else
Left_Right(p); // LR case
} else {
p->height = Max(Height(p->left), Height(p->right)) + 1;
}
} else if (x > p->data) {
p->right = Insertion(x, p->right);
// Cek balance factor
if (abs(Height(p->left) - Height(p->right)) == 2) {
if (x > p->right->data)
Left_Rotation(p); // RR case
else
Right_Left(p); // RL case
} else {
p->height = Max(Height(p->left), Height(p->right)) + 1;
}
}
}
return p;
}
📌 Contoh: Konstruksi AVL Tree
Insert urutan: 60, 50, 70, 40, 80, 30, 90, 20, 100
- Insert 60: Tree hanya berisi root
- Insert 50: 50 menjadi left child dari 60
- Insert 70: 70 menjadi right child dari 60, tree seimbang
- Insert 40: 40 menjadi left child dari 50, tree seimbang
- Insert 80: 80 menjadi right child dari 70, tree seimbang
- Insert 30: Menyebabkan ketidakseimbangan di node 60 (BF=+2), lakukan rotasi
- Insert 90, 20, 100: Setiap insert dicek dan dilakukan rotasi jika perlu
Hasil akhir adalah AVL Tree yang seimbang dengan height optimal.
Operasi Delete pada AVL Tree
Delete pada AVL Tree lebih kompleks karena harus mempertimbangkan berbagai kasus rebalancing setelah penghapusan.
5 Kasus Utama Deletion
Setiap kasus memiliki 2 variasi (deletion dari left atau right subtree), total 10 kasus:
Case 1: Equal Balance
Node memiliki balance yang sama (BF=0). Setelah deletion, cukup update BF dan stop. Tidak mempengaruhi node di atasnya.
Case 2: Previously Unbalanced
Deletion mengurangi tinggi subtree yang sebelumnya lebih tinggi. Update BF dan lanjutkan traverse ke atas karena dapat mempengaruhi node lain.
Case 3: Balanced Child
Right subtree lebih tinggi dan left subtree dari right child memiliki subtree yang sama tinggi. Lakukan single rotation dan stop (tidak mempengaruhi node di atas).
Case 4: Unbalanced Child
Right subtree lebih tinggi dan salah satu atau kedua subtree dari right child memiliki tinggi (h-1). Lakukan double rotation dan lanjutkan traverse ke atas.
Case 5: Right-Heavy Child
Right subtree lebih tinggi dan right subtree dari right child lebih tinggi dari left-nya. Lakukan single rotation dan lanjutkan traverse ke atas.
💡 Perbedaan dengan Insert
- Insert: Maksimal 1 rotasi untuk menyeimbangkan tree
- Delete: Dapat memerlukan rotasi hingga ke root (O(log n) rotasi)
- Delete harus menelusuri path dari node yang dihapus ke root untuk memeriksa dan memperbaiki keseimbangan
Kompleksitas AVL Tree
| Operasi | Time Complexity | Keterangan |
|---|---|---|
| Search | O(log n) | Dijamin karena tree seimbang |
| Insert | O(log n) | Maksimal 1 rotasi |
| Delete | O(log n) | Maksimal O(log n) rotasi |
| Space | O(n) | Overhead: height field per node |
Perbandingan BST vs AVL Tree
| Aspek | BST | AVL Tree |
|---|---|---|
| Kompleksitas Worst Case | O(n) | O(log n) |
| Balancing | Tidak ada | Otomatis (self-balancing) |
| Memory Overhead | Rendah | Sedang (height field) |
| Insert/Delete Speed | Lebih cepat | Sedikit lebih lambat (rotasi) |
| Search Speed | Bervariasi | Konsisten cepat |
| Use Case | Data random, jarang insert/delete | Aplikasi real-time, database index |
10. Kesimpulan
Tree adalah struktur data fundamental yang sangat penting dalam ilmu komputer. Dari tree dasar hingga AVL Tree, setiap evolusi struktur data ini dirancang untuk meningkatkan efisiensi operasi.
🌳 Tree Dasar
Memberikan dasar untuk struktur hierarkis, cocok untuk representasi struktur data seperti file system, organizational chart, dan XML/JSON parsing.
🌲 Binary Tree
Menyederhanakan implementasi dengan maksimal 2 children per node. Dasar untuk berbagai struktur data lanjutan.
🔍 BST
Menambahkan properti ordering untuk pencarian efisien. Ideal untuk aplikasi yang membutuhkan data terurut dan akses cepat.
⚖️ AVL Tree
Menjamin keseimbangan dan kompleksitas O(log n). Perfect untuk aplikasi real-time dan database yang memerlukan performa konsisten.
Kapan Menggunakan Apa?
Gunakan Tree Dasar untuk:
- Representasi hierarki organisasi
- File system navigation
- Parse tree dalam compiler
- Decision tree dalam AI
Gunakan Binary Tree untuk:
- Expression evaluation
- Huffman coding untuk kompresi data
- Binary heap untuk priority queue
- Syntax tree dalam compiler
Gunakan BST untuk:
- Implementasi set dan map
- Auto-complete features
- Data terurut dengan insert/delete cukup jarang
- Range query applications
Gunakan AVL Tree untuk:
- Database indexing
- Aplikasi real-time yang memerlukan performa konsisten
- Sistem dengan banyak read operations
- Implementasi associative arrays
- In-memory database dan caching systems
🎯 Key Takeaways
- Tree memberikan O(log n) time complexity untuk operasi dasar ketika seimbang
- BST mudah diimplementasikan namun dapat menjadi tidak seimbang
- AVL Tree mengatasi masalah keseimbangan dengan rotasi otomatis
- Trade-off: AVL Tree lebih kompleks namun menjamin performa konsisten
- Pemilihan struktur data harus disesuaikan dengan kebutuhan aplikasi

Komentar
Posting Komentar