Pelajari dasar-dasar Pohon Pencarian Biner (BST) dan implementasinya yang efisien dalam JavaScript. Panduan ini mencakup struktur, operasi, dan contoh praktis untuk pengembang global.
Pohon Pencarian Biner: Panduan Implementasi Komprehensif dalam JavaScript
Pohon Pencarian Biner (Binary Search Trees atau BST) adalah struktur data fundamental dalam ilmu komputer, yang banyak digunakan untuk pencarian, pengurutan, dan pengambilan data yang efisien. Struktur hierarkisnya memungkinkan kompleksitas waktu logaritmik dalam banyak operasi, menjadikannya alat yang ampuh untuk mengelola kumpulan data besar. Panduan ini memberikan gambaran komprehensif tentang BST dan menunjukkan implementasinya dalam JavaScript, yang ditujukan untuk para pengembang di seluruh dunia.
Memahami Pohon Pencarian Biner
Apa itu Pohon Pencarian Biner?
Pohon Pencarian Biner adalah struktur data berbasis pohon di mana setiap node memiliki paling banyak dua anak, yang disebut sebagai anak kiri dan anak kanan. Properti utama dari BST adalah bahwa untuk setiap node tertentu:
- Semua node di sub-pohon kiri memiliki kunci yang lebih kecil dari kunci node tersebut.
- Semua node di sub-pohon kanan memiliki kunci yang lebih besar dari kunci node tersebut.
Properti ini memastikan bahwa elemen-elemen dalam BST selalu terurut, memungkinkan pencarian dan pengambilan yang efisien.
Konsep Kunci
- Node: Unit dasar dalam pohon, berisi kunci (data) dan penunjuk ke anak kiri dan kanannya.
- Root: Node paling atas dalam pohon.
- Leaf: Node yang tidak memiliki anak.
- Subtree: Sebagian dari pohon yang berakar pada node tertentu.
- Height (Tinggi): Panjang jalur terpanjang dari root ke leaf.
- Depth (Kedalaman): Panjang jalur dari root ke node tertentu.
Mengimplementasikan Pohon Pencarian Biner di JavaScript
Mendefinisikan Kelas Node
Pertama, kita mendefinisikan kelas Node
untuk mewakili setiap node dalam BST. Setiap node akan berisi sebuah key
untuk menyimpan data dan penunjuk left
dan right
ke anak-anaknya.
class Node {
constructor(key) {
this.key = key;
this.left = null;
this.right = null;
}
}
Mendefinisikan Kelas Pohon Pencarian Biner
Selanjutnya, kita mendefinisikan kelas BinarySearchTree
. Kelas ini akan berisi node root dan metode untuk menyisipkan, mencari, menghapus, dan menelusuri pohon.
class BinarySearchTree {
constructor() {
this.root = null;
}
// Metode akan ditambahkan di sini
}
Penyisipan
Metode insert
menambahkan node baru dengan kunci yang diberikan ke BST. Proses penyisipan mempertahankan properti BST dengan menempatkan node baru di posisi yang sesuai relatif terhadap node yang ada.
insert(key) {
const newNode = new Node(key);
if (this.root === null) {
this.root = newNode;
} else {
this.insertNode(this.root, newNode);
}
}
insertNode(node, newNode) {
if (newNode.key < node.key) {
if (node.left === null) {
node.left = newNode;
} else {
this.insertNode(node.left, newNode);
}
} else {
if (node.right === null) {
node.right = newNode;
} else {
this.insertNode(node.right, newNode);
}
}
}
Contoh: Menyisipkan nilai ke dalam BST
const bst = new BinarySearchTree();
bst.insert(11);
bst.insert(7);
bst.insert(15);
bst.insert(5);
bst.insert(3);
bst.insert(9);
bst.insert(8);
bst.insert(10);
bst.insert(13);
bst.insert(12);
bst.insert(14);
bst.insert(20);
bst.insert(18);
bst.insert(25);
Pencarian
Metode search
memeriksa apakah sebuah node dengan kunci yang diberikan ada di dalam BST. Metode ini menelusuri pohon, membandingkan kunci dengan kunci node saat ini dan bergerak ke sub-pohon kiri atau kanan yang sesuai.
search(key) {
return this.searchNode(this.root, key);
}
searchNode(node, key) {
if (node === null) {
return false;
}
if (key < node.key) {
return this.searchNode(node.left, key);
} else if (key > node.key) {
return this.searchNode(node.right, key);
} else {
return true;
}
}
Contoh: Mencari nilai di dalam BST
console.log(bst.search(9)); // Output: true
console.log(bst.search(2)); // Output: false
Penghapusan
Metode remove
menghapus sebuah node dengan kunci yang diberikan dari BST. Ini adalah operasi yang paling kompleks karena perlu mempertahankan properti BST saat menghapus node. Ada tiga kasus yang perlu dipertimbangkan:
- Kasus 1: Node yang akan dihapus adalah node daun. Cukup hapus saja.
- Kasus 2: Node yang akan dihapus memiliki satu anak. Ganti node tersebut dengan anaknya.
- Kasus 3: Node yang akan dihapus memiliki dua anak. Temukan suksesor in-order (node terkecil di sub-pohon kanan), ganti node dengan suksesor tersebut, lalu hapus suksesornya.
remove(key) {
this.root = this.removeNode(this.root, key);
}
removeNode(node, key) {
if (node === null) {
return null;
}
if (key < node.key) {
node.left = this.removeNode(node.left, key);
return node;
} else if (key > node.key) {
node.right = this.removeNode(node.right, key);
return node;
} else {
// kunci sama dengan node.key
// kasus 1 - node daun
if (node.left === null && node.right === null) {
node = null;
return node;
}
// kasus 2 - node hanya memiliki 1 anak
if (node.left === null) {
node = node.right;
return node;
} else if (node.right === null) {
node = node.left;
return node;
}
// kasus 3 - node memiliki 2 anak
const aux = this.findMinNode(node.right);
node.key = aux.key;
node.right = this.removeNode(node.right, aux.key);
return node;
}
}
findMinNode(node) {
let current = node;
while (current != null && current.left != null) {
current = current.left;
}
return current;
}
Contoh: Menghapus nilai dari BST
bst.remove(7);
console.log(bst.search(7)); // Output: false
Penelusuran Pohon
Penelusuran pohon melibatkan kunjungan ke setiap node di pohon dalam urutan tertentu. Ada beberapa metode penelusuran umum:
- In-order: Mengunjungi sub-pohon kiri, lalu node, lalu sub-pohon kanan. Ini menghasilkan kunjungan node dalam urutan menaik.
- Pre-order: Mengunjungi node, lalu sub-pohon kiri, lalu sub-pohon kanan.
- Post-order: Mengunjungi sub-pohon kiri, lalu sub-pohon kanan, lalu node.
inOrderTraverse(callback) {
this.inOrderTraverseNode(this.root, callback);
}
inOrderTraverseNode(node, callback) {
if (node !== null) {
this.inOrderTraverseNode(node.left, callback);
callback(node.key);
this.inOrderTraverseNode(node.right, callback);
}
}
preOrderTraverse(callback) {
this.preOrderTraverseNode(this.root, callback);
}
preOrderTraverseNode(node, callback) {
if (node !== null) {
callback(node.key);
this.preOrderTraverseNode(node.left, callback);
this.preOrderTraverseNode(node.right, callback);
}
}
postOrderTraverse(callback) {
this.postOrderTraverseNode(this.root, callback);
}
postOrderTraverseNode(node, callback) {
if (node !== null) {
this.postOrderTraverseNode(node.left, callback);
this.postOrderTraverseNode(node.right, callback);
callback(node.key);
}
}
Contoh: Menelusuri BST
const printNode = (value) => console.log(value);
bst.inOrderTraverse(printNode); // Output: 3 5 8 9 10 11 12 13 14 15 18 20 25
bst.preOrderTraverse(printNode); // Output: 11 5 3 9 8 10 15 13 12 14 20 18 25
bst.postOrderTraverse(printNode); // Output: 3 8 10 9 12 14 13 18 25 20 15 11
Nilai Minimum dan Maksimum
Menemukan nilai minimum dan maksimum dalam BST sangat mudah, berkat sifatnya yang terurut.
min() {
return this.minNode(this.root);
}
minNode(node) {
let current = node;
while (current !== null && current.left !== null) {
current = current.left;
}
return current;
}
max() {
return this.maxNode(this.root);
}
maxNode(node) {
let current = node;
while (current !== null && current.right !== null) {
current = current.right;
}
return current;
}
Contoh: Menemukan nilai minimum dan maksimum
console.log(bst.min().key); // Output: 3
console.log(bst.max().key); // Output: 25
Aplikasi Praktis Pohon Pencarian Biner
Pohon Pencarian Biner digunakan dalam berbagai aplikasi, termasuk:
- Basis Data: Pengindeksan dan pencarian data. Sebagai contoh, banyak sistem basis data menggunakan variasi BST, seperti B-tree, untuk menemukan catatan secara efisien. Pertimbangkan skala global basis data yang digunakan oleh perusahaan multinasional; pengambilan data yang efisien adalah hal yang terpenting.
- Kompilator: Tabel simbol, yang menyimpan informasi tentang variabel dan fungsi.
- Sistem Operasi: Penjadwalan proses dan manajemen memori.
- Mesin Pencari: Mengindeks halaman web dan memeringkat hasil pencarian.
- Sistem File: Mengatur dan mengakses file. Bayangkan sebuah sistem file di server yang digunakan secara global untuk menghosting situs web; struktur berbasis BST yang terorganisir dengan baik membantu dalam menyajikan konten dengan cepat.
Pertimbangan Kinerja
Kinerja BST tergantung pada strukturnya. Dalam skenario kasus terbaik, BST yang seimbang memungkinkan kompleksitas waktu logaritmik untuk operasi penyisipan, pencarian, dan penghapusan. Namun, dalam skenario kasus terburuk (misalnya, pohon yang miring), kompleksitas waktu dapat menurun menjadi waktu linear.
Pohon Seimbang vs. Tidak Seimbang
BST yang seimbang adalah pohon di mana tinggi sub-pohon kiri dan kanan dari setiap node berbeda paling banyak satu. Algoritma penyeimbangan diri, seperti pohon AVL dan pohon Merah-Hitam, memastikan bahwa pohon tetap seimbang, memberikan kinerja yang konsisten. Wilayah yang berbeda mungkin memerlukan tingkat optimisasi yang berbeda berdasarkan beban pada server; penyeimbangan membantu mempertahankan kinerja di bawah penggunaan global yang tinggi.
Kompleksitas Waktu
- Penyisipan: O(log n) rata-rata, O(n) dalam kasus terburuk.
- Pencarian: O(log n) rata-rata, O(n) dalam kasus terburuk.
- Penghapusan: O(log n) rata-rata, O(n) dalam kasus terburuk.
- Penelusuran: O(n), di mana n adalah jumlah node di pohon.
Konsep Lanjutan BST
Pohon yang Menyeimbangkan Diri
Pohon yang menyeimbangkan diri adalah BST yang secara otomatis menyesuaikan strukturnya untuk menjaga keseimbangan. Hal ini memastikan bahwa tinggi pohon tetap logaritmik, memberikan kinerja yang konsisten untuk semua operasi. Pohon penyeimbang diri yang umum termasuk pohon AVL dan pohon Merah-Hitam.
Pohon AVL
Pohon AVL menjaga keseimbangan dengan memastikan bahwa perbedaan tinggi antara sub-pohon kiri dan kanan dari setiap node paling banyak satu. Ketika keseimbangan ini terganggu, rotasi dilakukan untuk mengembalikan keseimbangan.
Pohon Merah-Hitam
Pohon Merah-Hitam menggunakan properti warna (merah atau hitam) untuk menjaga keseimbangan. Mereka lebih kompleks daripada pohon AVL tetapi menawarkan kinerja yang lebih baik dalam skenario tertentu.
Contoh Kode JavaScript: Implementasi Lengkap Pohon Pencarian Biner
class Node {
constructor(key) {
this.key = key;
this.left = null;
this.right = null;
}
}
class BinarySearchTree {
constructor() {
this.root = null;
}
insert(key) {
const newNode = new Node(key);
if (this.root === null) {
this.root = newNode;
} else {
this.insertNode(this.root, newNode);
}
}
insertNode(node, newNode) {
if (newNode.key < node.key) {
if (node.left === null) {
node.left = newNode;
} else {
this.insertNode(node.left, newNode);
}
} else {
if (node.right === null) {
node.right = newNode;
} else {
this.insertNode(node.right, newNode);
}
}
}
search(key) {
return this.searchNode(this.root, key);
}
searchNode(node, key) {
if (node === null) {
return false;
}
if (key < node.key) {
return this.searchNode(node.left, key);
} else if (key > node.key) {
return this.searchNode(node.right, key);
} else {
return true;
}
}
remove(key) {
this.root = this.removeNode(this.root, key);
}
removeNode(node, key) {
if (node === null) {
return null;
}
if (key < node.key) {
node.left = this.removeNode(node.left, key);
return node;
} else if (key > node.key) {
node.right = this.removeNode(node.right, key);
return node;
} else {
// kunci sama dengan node.key
// kasus 1 - node daun
if (node.left === null && node.right === null) {
node = null;
return node;
}
// kasus 2 - node hanya memiliki 1 anak
if (node.left === null) {
node = node.right;
return node;
} else if (node.right === null) {
node = node.left;
return node;
}
// kasus 3 - node memiliki 2 anak
const aux = this.findMinNode(node.right);
node.key = aux.key;
node.right = this.removeNode(node.right, aux.key);
return node;
}
}
findMinNode(node) {
let current = node;
while (current != null && current.left != null) {
current = current.left;
}
return current;
}
min() {
return this.minNode(this.root);
}
minNode(node) {
let current = node;
while (current !== null && current.left !== null) {
current = current.left;
}
return current;
}
max() {
return this.maxNode(this.root);
}
maxNode(node) {
let current = node;
while (current !== null && current.right !== null) {
current = current.right;
}
return current;
}
inOrderTraverse(callback) {
this.inOrderTraverseNode(this.root, callback);
}
inOrderTraverseNode(node, callback) {
if (node !== null) {
this.inOrderTraverseNode(node.left, callback);
callback(node.key);
this.inOrderTraverseNode(node.right, callback);
}
}
preOrderTraverse(callback) {
this.preOrderTraverseNode(this.root, callback);
}
preOrderTraverseNode(node, callback) {
if (node !== null) {
callback(node.key);
this.preOrderTraverseNode(node.left, callback);
this.preOrderTraverseNode(node.right, callback);
}
}
postOrderTraverse(callback) {
this.postOrderTraverseNode(this.root, callback);
}
postOrderTraverseNode(node, callback) {
if (node !== null) {
this.postOrderTraverseNode(node.left, callback);
this.postOrderTraverseNode(node.right, callback);
callback(node.key);
}
}
}
// Contoh Penggunaan
const bst = new BinarySearchTree();
bst.insert(11);
bst.insert(7);
bst.insert(15);
bst.insert(5);
bst.insert(3);
bst.insert(9);
bst.insert(8);
bst.insert(10);
bst.insert(13);
bst.insert(12);
bst.insert(14);
bst.insert(20);
bst.insert(18);
bst.insert(25);
const printNode = (value) => console.log(value);
console.log("Penelusuran in-order:");
bst.inOrderTraverse(printNode);
console.log("Penelusuran pre-order:");
bst.preOrderTraverse(printNode);
console.log("Penelusuran post-order:");
bst.postOrderTraverse(printNode);
console.log("Nilai minimum:", bst.min().key);
console.log("Nilai maksimum:", bst.max().key);
console.log("Cari angka 9:", bst.search(9));
console.log("Cari angka 2:", bst.search(2));
bst.remove(7);
console.log("Cari angka 7 setelah dihapus:", bst.search(7));
Kesimpulan
Pohon Pencarian Biner adalah struktur data yang kuat dan serbaguna dengan banyak aplikasi. Panduan ini telah memberikan gambaran komprehensif tentang BST, yang mencakup struktur, operasi, dan implementasinya dalam JavaScript. Dengan memahami prinsip dan teknik yang dibahas dalam panduan ini, pengembang di seluruh dunia dapat secara efektif menggunakan BST untuk menyelesaikan berbagai masalah dalam pengembangan perangkat lunak. Dari mengelola basis data global hingga mengoptimalkan algoritma pencarian, pengetahuan tentang BST adalah aset yang tak ternilai bagi setiap programmer.
Saat Anda melanjutkan perjalanan Anda dalam ilmu komputer, menjelajahi konsep-konsep lanjutan seperti pohon yang menyeimbangkan diri dan berbagai implementasinya akan lebih meningkatkan pemahaman dan kemampuan Anda. Teruslah berlatih dan bereksperimen dengan berbagai skenario untuk menguasai seni menggunakan Pohon Pencarian Biner secara efektif.