Pertemuan 12 - BTree & BST

 

BTree & BST di C++

Nama: Hadryan Rizky Dimas Saputra
NRP: 5025251027
Kelas: Struktur Data (D) 2026
Pertemuan: 12

Source Code: pertemuan_12

1. Implementasi BTree pada C++

Deskripsi (Fungsi) Program: 

B-Tree adalah struktur data pohon pencarian (search tree) yang dapat menyeimbangkan dirinya sendiri (self-balancing). Berbeda dengan Binary Search Tree biasa di mana satu node maksimal hanya memiliki 2 anak (kiri dan kanan), B-Tree merupakan multiway search tree yang dirancang agar satu node dapat menampung banyak key (nilai) dan memiliki banyak anak (children).

Karakteristik Utama B-Tree:

1. Derajat Minimum (Minimum Degree): Biasanya disimbolkan dengan t (atau minDegree dalam kode). Nilai ini menentukan batas jumlah key dan child pada setiap node. Maksimal key per node = 2t - 1. Maksimal child per node = 2t

2. Keseimbangan Sempurna: Semua leaf node (daun terbawah) pasti berada pada level atau kedalaman yang sama.

3. Mekanisme Split (Pemecahan): Ketika sebuah node sudah penuh (mencapai batas maksimal key) dan ada data baru yang ingin dimasukkan, node tersebut akan terbelah menjadi dua. Key yang berada tepat di tengah akan "naik" ke parent node.

4. Efisiensi: Karena B-Tree lebih "lebar" dan tidak terlalu dalam, operasi pencarian (Search), penyisipan (Insert), dan penghapusan (Delete) sangat cepat dengan kompleksitas waktu O(log n). Struktur ini sangat sering digunakan dalam sistem database dan file system.

Kemudian implementasi program yang saya buat terbagi menjadi dua kelas utama: BTreeNode yang mewakili satu lingkaran/blok dalam pohon, dan BTree yang bertindak sebagai pengelola utama.

1. Kelas BTreeNode (Representasi Node)

Kelas ini bertanggung jawab atas data dan operasi yang terjadi di dalam satu node spesifik. 

- BTreeNode(int minDegree, bool isLeaf) (Constructor): Saat node baru dibuat, fungsi ini mengalokasikan memori untuk array keys dan array children berdasarkan aturan derajat minimum.

- traverse(): Fungsi untuk mencetak seluruh isi pohon secara berurutan dari terkecil ke terbesar (inorder traversal). Fungsi ini membaca child sebelah kiri, mencetak key, lalu lanjut ke child kanannya.

- search(int key): Melakukan pencarian data. Fungsi ini mencari posisi key. Jika tidak ketemu di node saat ini, ia akan turun ke child yang rentang nilainya sesuai dengan key yang dicari.

- insertNonFull(int key): Fungsi ini hanya dipanggil jika node dipastikan belum penuh. Jika node adalah leaf, data langsung diselipkan pada urutan yang benar. Jika bukan leaf, ia akan mencari jalan turun ke child yang tepat (dan mengecek apakah child tersebut perlu di-split sebelum turun).

- splitChild(int childIndex, BTreeNode* fullChild): Ini adalah jantung dari algoritma B-Tree. Jika fullChild sudah penuh, fungsi ini akan membaginya menjadi dua node terpisah. Nilai yang berada di tengah akan dipindahkan ke node parent untuk menjaga struktur tetap seimbang.

2. Kelas BTree (Pengelola Pohon)

Kelas ini adalah jembatan penghubung antara programmer dan struktur pohon secara keseluruhan. Ia hanya menyimpan pointer ke titik awal pohon, yaitu root.

- BTree(int minDegree) (Constructor): Menginisialisasi pohon kosong dengan menetapkan nilai root menjadi kosong (nullptr).

- search(int key) & traverse(): Fungsi pembungkus (wrapper). Keduanya hanya memanggil fungsi search dan traverse milik root (jika pohon tidak kosong).

- insert(int key): Fungsi utama penyisipan dari luar: Jika pohon kosong, ia membuat root baru. Jika root yang ada saat ini sudah penuh, fungsi ini akan membuat node root baru, lalu memanggil splitChild untuk membelah root lama, kemudian baru memasukkan data. Jika root belum penuh, ia langsung mengoper tugasnya ke insertNonFull.

Code:

#include <bits/stdc++.h>

class BTreeNode {
private:
int* keys;
int minDegree;
BTreeNode** children;
int numKeys;
bool isLeaf;

public:
BTreeNode(int minDegree, bool isLeaf);

void traverse() const;
BTreeNode* search(int key);
void insertNonFull(int key);
void splitChild(int childIndex, BTreeNode* fullChild);

friend class BTree;
};

class BTree {
private:
BTreeNode* root;
int minDegree;

public:
explicit BTree(int minDegree) : root(nullptr), minDegree(minDegree) {}

void traverse() const {
if (root != nullptr) {
root->traverse();
}
}

BTreeNode* search(int key) {
return (root == nullptr) ? nullptr : root->search(key);
}

void insert(int key);
};

BTreeNode::BTreeNode(int minDegree, bool isLeaf)
: minDegree(minDegree), isLeaf(isLeaf), numKeys(0) {
keys = new int[2 * minDegree - 1];
children = new BTreeNode*[2 * minDegree];
}

void BTreeNode::traverse() const {
int i;
for (i = 0; i < numKeys; i++) {
if (!isLeaf) {
children[i]->traverse();
}
std::cout << keys[i] << " ";
}
if (!isLeaf) {
children[i]->traverse();
}
}

BTreeNode* BTreeNode::search(int key) {
int i = 0;
while (i < numKeys && key > keys[i]) {
i++;
}

if (i < numKeys && keys[i] == key) {
return this;
}

if (isLeaf) {
return nullptr;
}

return children[i]->search(key);
}

void BTree::insert(int key) {
if (root == nullptr) {
root = new BTreeNode(minDegree, true);
root->keys[0] = key;
root->numKeys = 1;
return;
}

if (root->numKeys == 2 * minDegree - 1) {
BTreeNode* newRoot = new BTreeNode(minDegree, false);
newRoot->children[0] = root;
newRoot->splitChild(0, root);

int i = (newRoot->keys[0] < key) ? 1 : 0;
newRoot->children[i]->insertNonFull(key);

root = newRoot;
} else {
root->insertNonFull(key);
}
}

void BTreeNode::insertNonFull(int key) {
int i = numKeys - 1;

if (isLeaf) {
while (i >= 0 && keys[i] > key) {
keys[i + 1] = keys[i];
i--;
}
keys[i + 1] = key;
numKeys++;
} else {
while (i >= 0 && keys[i] > key) {
i--;
}

if (children[i + 1]->numKeys == 2 * minDegree - 1) {
splitChild(i + 1, children[i + 1]);

if (keys[i + 1] < key) {
i++;
}
}
children[i + 1]->insertNonFull(key);
}
}

void BTreeNode::splitChild(int childIndex, BTreeNode* fullChild) {
BTreeNode* newNode = new BTreeNode(fullChild->minDegree, fullChild->isLeaf);
newNode->numKeys = minDegree - 1;

for (int j = 0; j < minDegree - 1; j++) {
newNode->keys[j] = fullChild->keys[j + minDegree];
}

if (!fullChild->isLeaf) {
for (int j = 0; j < minDegree; j++) {
newNode->children[j] = fullChild->children[j + minDegree];
}
}

fullChild->numKeys = minDegree - 1;

for (int j = numKeys; j >= childIndex + 1; j--) {
children[j + 1] = children[j];
}
children[childIndex + 1] = newNode;

for (int j = numKeys - 1; j >= childIndex; j--) {
keys[j + 1] = keys[j];
}
keys[childIndex] = fullChild->keys[minDegree - 1];
numKeys++;
}

int main(void) {
BTree tree(2);

int valuesToInsert[] = {10, 20, 30, 40, 50, 60, 70, 80};
for (int value : valuesToInsert) {
tree.insert(value);
}

std::cout << "B-Tree Traversal: ";
tree.traverse();
std::cout << "\n";

int searchKey = 50;
std::cout << "Searching for " << searchKey << "...\n";
if (tree.search(searchKey) != nullptr) {
std::cout << "Key " << searchKey << " found in the B-Tree.\n";
} else {
std::cout << "Key " << searchKey << " NOT found.\n";
}

return 0;
}

Hasil/Output Program:

Penjelasan Program:

Baris Kode

Penjelasan

#include <bits/stdc++.h>

Mengimpor semua standar library C++ sekaligus (termasuk iostream).

class BTreeNode {

Memulai deklarasi kelas BTreeNode untuk merepresentasikan satu blok/node.

private:

Akses modifier: atribut di bawah ini hanya bisa diakses dari dalam kelas ini.

int* keys;

Pointer untuk membuat array dinamis yang menyimpan nilai (keys) dalam node.

int minDegree;

Derajat minimum (t) yang menentukan batas kapasitas key dan child.

BTreeNode** children;

Pointer ke array of pointers untuk menyimpan alamat node anak (children).

int numKeys;

Menyimpan jumlah key yang saat ini sedang terisi di dalam node.

bool isLeaf;

Penanda boolean (true jika node adalah daun/paling bawah, false jika bukan).

public:

Akses modifier: fungsi di bawah ini bisa diakses dari luar kelas.

BTreeNode(int minDegree, bool isLeaf);

Deklarasi constructor untuk menginisialisasi node baru.

void traverse() const;

Deklarasi fungsi untuk mencetak seluruh key secara berurutan (inorder).

BTreeNode* search(int key);

Deklarasi fungsi untuk mencari key tertentu dan mengembalikan alamat nodenya.

void insertNonFull(int key);

Deklarasi fungsi penyisipan key saat dipastikan node tersebut belum penuh.

void splitChild(int childIndex, BTreeNode* fullChild);

Deklarasi fungsi untuk membelah child node yang sudah penuh kapasitasnya.

friend class BTree;

Memberikan izin kelas BTree untuk mengakses atribut private dari BTreeNode.

};

Menutup deklarasi kelas BTreeNode.

class BTree {

Memulai deklarasi kelas BTree sebagai pengelola pohon utama.

private:

Atribut internal kelas pohon.

BTreeNode* root;

Pointer yang menunjuk ke node paling atas (akar/root) dari B-Tree.

int minDegree;

Derajat minimum (t) untuk seluruh pohon.

public:

Fungsi-fungsi yang bisa dipanggil oleh pengguna dari main.

explicit BTree(int minDegree)

Constructor B-Tree dengan parameter derajat minimum.

: root(nullptr), minDegree(minDegree) {}

Inisialisasi awal: root kosong (nullptr) dan minDegree diatur sesuai input.

void traverse() const {

Fungsi pembungkus (wrapper) untuk mencetak isi pohon.

if (root != nullptr) {

Mengecek apakah pohon memiliki setidaknya satu node (tidak kosong).

root->traverse();

Jika tidak kosong, panggil fungsi traverse milik node root.

}

Menutup blok kondisi if.

}

Menutup fungsi traverse kelas BTree.

BTreeNode* search(int key) {

Fungsi pembungkus untuk mencari key dari akar pohon.

return (root == nullptr) ? nullptr : root->search(key);

Jika kosong, kembalikan nullptr. Jika ada, teruskan pencarian ke root.

}

Menutup fungsi search.

void insert(int key);

Deklarasi fungsi utama untuk memasukkan data baru ke dalam pohon.

};

Menutup deklarasi kelas BTree.

BTreeNode::BTreeNode(int minDegree, bool isLeaf)

Implementasi constructor kelas BTreeNode.

: minDegree(minDegree), isLeaf(isLeaf), numKeys(0) {

Inisialisasi awal atribut (jumlah key diatur 0).

keys = new int[2 * minDegree - 1];

Mengalokasikan memori array untuk keys (kapasitas maksimal 2t-1).

children = new BTreeNode*[2 * minDegree];

Mengalokasikan memori array untuk pointer children (kapasitas maksimal 2t).

}

Menutup constructor.

void BTreeNode::traverse() const {

Implementasi pencetakan isi node.

int i;

Inisialisasi variabel iterasi i.

for (i = 0; i < numKeys; i++) {

Looping sebanyak jumlah key yang ada di dalam node saat ini.

if (!isLeaf) {

Jika node ini bukan daun (punya anak)...

children[i]->traverse();

...kunjungi anak sebelah kiri dari key ke-i terlebih dahulu.

}

Menutup pengecekan daun.

std::cout << keys[i] << " ";

Cetak nilai key ke-i ke layar.

}

Menutup perulangan for.

if (!isLeaf) {

Mengecek lagi setelah loop selesai.

children[i]->traverse();

Kunjungi anak paling kanan yang tersisa.

}

Menutup kondisi.

}

Menutup fungsi traverse.

BTreeNode* BTreeNode::search(int key) {

Implementasi logika pencarian nilai.

int i = 0;

Indeks awal disetel 0.

while (i < numKeys && key > keys[i]) {

Selama indeks valid dan nilai yang dicari lebih besar dari key saat ini...

i++;

...geser indeks ke kanan.

}

Menutup perulangan while.

if (i < numKeys && keys[i] == key) {

Jika berhenti karena key yang dicari sama persis dengan key di indeks i...

return this;

...kembalikan pointer ke node ini (pencarian berhasil).

}

Menutup kondisi.

if (isLeaf) {

Jika key tidak ditemukan dan node ini tidak punya anak lagi (leaf)...

return nullptr;

...kembalikan nullptr (data tidak ada di pohon).

}

Menutup kondisi.

return children[i]->search(key);

Jika bukan daun, turun dan teruskan pencarian ke anak ke-i.

}

Menutup fungsi search.

void BTree::insert(int key) {

Logika utama untuk menangani penyisipan data baru.

if (root == nullptr) {

Jika pohon masih benar-benar kosong...

root = new BTreeNode(minDegree, true);

...buat node pertama sebagai root sekaligus leaf.

root->keys[0] = key;

Masukkan data pertama ke indeks ke-0.

root->numKeys = 1;

Perbarui jumlah key di root menjadi 1.

return;

Keluar dari fungsi penyisipan.

}

Menutup kondisi pohon kosong.

if (root->numKeys == 2 * minDegree - 1) {

Jika pohon tidak kosong, cek apakah kapasitas root sudah penuh maksimal (2t-1).

BTreeNode* newRoot = new BTreeNode(minDegree, false);

Jika penuh, buat node kosong baru (bukan leaf) untuk jadi root yang baru.

newRoot->children[0] = root;

Jadikan root lama sebagai anak pertama dari root baru.

newRoot->splitChild(0, root);

Belah root lama agar setengah datanya naik ke root baru.

int i = (newRoot->keys[0] < key) ? 1 : 0;

Tentukan cabang mana yang akan dituju (0 jika key lebih kecil, 1 jika lebih besar).

newRoot->children[i]->insertNonFull(key);

Masukkan data ke cabang anak yang telah dipilih tersebut.

root = newRoot;

Perbarui pointer root utama agar menunjuk ke newRoot.

} else {

Jika kondisi root saat ini belum penuh...

root->insertNonFull(key);

...langsung sisipkan data menggunakan fungsi insertNonFull.

}

Menutup blok percabangan penuh/tidak.

}

Menutup fungsi insert.

void BTreeNode::insertNonFull(int key) {

Menyisipkan key pada node yang sudah dipastikan punya ruang kosong.

int i = numKeys - 1;

Mulai pengecekan dari key yang paling kanan/terbesar.

if (isLeaf) {

Jika node ini adalah daun (tempat akhir penyimpanan data)...

while (i >= 0 && keys[i] > key) {

Geser key yang lebih besar dari data baru ke arah kanan.

keys[i + 1] = keys[i];

Pindahkan nilai key ke indeks kanannya.

i--;

Mundur ke kiri untuk mengecek key selanjutnya.

}

Menutup looping pergeseran.

keys[i + 1] = key;

Sisipkan data baru di ruang kosong yang tercipta.

numKeys++;

Tambahkan penghitung jumlah key node ini sebesar 1.

} else {

Jika node ini bukan daun (hanya jalur lewat)...

while (i >= 0 && keys[i] > key) {

Cari indeks anak mana yang harus dituju.

i--;

Kurangi indeks selama key node lebih besar dari data baru.

}

Menutup looping pencarian cabang.

if (children[i + 1]->numKeys == 2 * minDegree - 1) {

Cek apakah anak yang mau dituju tersebut ternyata sudah penuh.

splitChild(i + 1, children[i + 1]);

Jika anak penuh, pecah/belah anak tersebut menjadi dua.

if (keys[i + 1] < key) {

Setelah dipecah, key tengah naik. Cek kembali arah cabangnya.

i++;

Sesuaikan indeks jika ternyata data lebih besar dari key yang baru naik.

}

Menutup penyesuaian arah.

}

Menutup pengecekan kapasitas anak.

children[i + 1]->insertNonFull(key);

Lanjutkan proses penyisipan ke anak yang tepat.

}

Menutup blok bukan daun.

}

Menutup fungsi insertNonFull.

void BTreeNode::splitChild(int childIndex, BTreeNode* fullChild) {

Memecah node anak yang penuh menjadi dua bagian (kiri dan kanan).

BTreeNode* newNode = new BTreeNode(fullChild->minDegree, fullChild->isLeaf);

Buat node baru (newNode) yang akan menjadi belahan kanan dari fullChild.

newNode->numKeys = minDegree - 1;

Node baru akan menampung tepat setengah (t-1) key dari bagian kanan node penuh.

for (int j = 0; j < minDegree - 1; j++) {

Looping untuk memindahkan setengah key bagian kanan...

newNode->keys[j] = fullChild->keys[j + minDegree];

...dari fullChild ke newNode.

}

Menutup pemindahan key.

if (!fullChild->isLeaf) {

Jika node yang dibelah bukan daun (punya anak)...

for (int j = 0; j < minDegree; j++) {

Looping untuk memindahkan pointer anak bagian kanan...

newNode->children[j] = fullChild->children[j + minDegree];

...ke node baru (newNode).

}

Menutup pemindahan pointer anak.

}

Menutup kondisi.

fullChild->numKeys = minDegree - 1;

Kurangi jumlah key di node lama (fullChild) menjadi setengah (t-1).

for (int j = numKeys; j >= childIndex + 1; j--) {

Geser pointer anak di node parent ini ke kanan...

children[j + 1] = children[j];

...untuk memberi tempat pada pointer newNode.

}

Menutup pergeseran pointer anak.

children[childIndex + 1] = newNode;

Sambungkan newNode ke array anak di parent ini.

for (int j = numKeys - 1; j >= childIndex; j--) {

Geser keys di parent ini ke kanan...

keys[j + 1] = keys[j];

...untuk memberi tempat pada key tengah yang akan naik.

}

Menutup pergeseran keys.

keys[childIndex] = fullChild->keys[minDegree - 1];

Pindahkan nilai tengah dari fullChild naik ke posisi kosong di parent.

numKeys++;

Tambahkan penghitung jumlah key di parent sebesar 1.

}

Menutup fungsi splitChild.

int main(void) {

Fungsi utama program dijalankan dari sini.

BTree tree(2);

Membentuk objek B-Tree bernama tree dengan minimum derajat 2.

int valuesToInsert[] = {10, 20, 30, 40, 50, 60, 70, 80};

Array yang berisi sekumpulan angka untuk dimasukkan.

for (int value : valuesToInsert) {

Menggunakan range-based for loop untuk membaca tiap angka.

tree.insert(value);

Memasukkan angka tersebut ke dalam B-Tree.

}

Menutup perulangan penyisipan.

std::cout << "B-Tree Traversal: ";

Mencetak teks awal sebelum menampilkan isi pohon.

tree.traverse();

Memanggil fungsi untuk mencetak struktur isi pohon secara berurutan.

std::cout << "\n";

Mencetak baris baru (enter).

int searchKey = 50;

Menentukan nilai yang ingin dicari (contoh: 50).

std::cout << "Searching for " << searchKey << "...\n";

Mencetak informasi proses pencarian.

if (tree.search(searchKey) != nullptr) {

Jika fungsi pencarian tidak mengembalikan kosong (artinya data ketemu)...

std::cout << "Key " << searchKey << " found in the B-Tree.\n";

...cetak pesan berhasil ditemukan.

} else {

Jika fungsi mengembalikan kosong (nullptr)...

std::cout << "Key " << searchKey << " NOT found.\n";

...cetak pesan data tidak ditemukan.

}

Menutup blok pengecekan hasil pencarian.

return 0;

Mengembalikan 0, menandakan program selesai tanpa masalah.

}

Menutup fungsi main.


2. Implementasi BST pada C++

Deskripsi (Fungsi) Program: 

Binary Search Tree (BST) adalah struktur data pohon biner yang memiliki aturan pengurutan khusus untuk mempercepat proses pencarian, penyisipan, dan penghapusan data. Aturan utamanya adalah: nilai pada subtree kiri harus selalu lebih kecil dari nilai root (induknya), dan nilai pada subtree kanan harus selalu lebih besar dari root.

1. Insert (Penyisipan): Menambahkan node baru dengan membandingkan nilainya terhadap root; jika lebih kecil turun ke kiri, jika lebih besar turun ke kanan, hingga menemukan posisi kosong (leaf).

2. Search (Pencarian): Mencari nilai tertentu dengan prinsip membagi ruang pencarian. Pencarian menelusuri cabang kiri atau kanan tergantung apakah nilai yang dicari lebih kecil atau lebih besar dari node saat ini.

3. Delete (Penghapusan): Merupakan operasi paling kompleks karena harus mempertahankan struktur BST. Ada 3 skenario yang terjadi:

    - Menghapus Leaf Node: Node tidak punya anak, sehingga bisa langsung dihapus.

    - Menghapus Node dengan 1 Anak: Node dihapus, dan posisinya digantikan langsung oleh anak tunggalnya tersebut.

    - Menghapus Node dengan 2 Anak: Node digantikan oleh Inorder Successor (nilai terkecil dari subtree sebelah kanan), kemudian successor di posisi asalnya tersebut dihapus.

4. Traversal: Cara mengunjungi seluruh node di dalam BST. Terdapat 3 jenis yaitu Inorder (Kiri-Root-Kanan), Preorder (Root-Kiri-Kanan), dan Postorder (Kiri-Kanan-Root).


Deskripsi Fungsi dalam Program C++ BST

1. Struktur Node

Node (Struct): Merepresentasikan satu titik/lingkaran dalam pohon. Memiliki 3 bagian utama: value untuk menyimpan angka, left (pointer ke anak kiri), dan right (pointer ke anak kanan).

2. Fungsi Internal / Rekursif (private)

Fungsi-fungsi ini bekerja di balik layar dan membutuhkan pointer Node secara spesifik untuk memanipulasi struktur pohon:

- insert(Node* node, int value): Bergerak turun dari root ke bawah. Jika nilai baru lebih kecil, fungsi memanggil dirinya sendiri ke node->left. Jika mencapai ujung bawah (nullptr), ia membuat Node baru.

- search(Node* node, int value): Mengecek nilai node saat ini. Jika cocok, mengembalikan node tersebut. Jika tidak, akan memanggil dirinya sendiri ke kiri atau ke kanan hingga menemukan nilai tersebut atau mencapai nullptr (tidak ditemukan).

- findMin(Node* node): Mencari Inorder Successor dengan cara terus bergerak ke anak kiri (node->left) hingga mentok. Nilai ujung kiri ini adalah nilai terkecil pada suatu cabang/subtree.

- remove(Node* node, int value): Melakukan operasi Delete berdasarkan 3 kondisi dari dokumen. Jika node memiliki 2 anak, fungsi ini akan memanggil findMin untuk mencari successor, menimpa nilai node saat ini dengan nilai successor, lalu memanggil remove lagi untuk menghapus successor di bawahnya.

- inorder, preorder, postorder: Mengunjungi dan mencetak node menggunakan rekursi berdasar aturan letak pencetakan node->value (di tengah, di awal, atau di akhir pemanggilan).

3. Fungsi Antarmuka / Pembungkus (public)

- BST() (Constructor): Mengatur pohon awal dengan root = nullptr (pohon kosong).

- insert(int value): Memanggil fungsi rekursif insert dengan memberikan root sebagai titik awal.

- search(int value): Mengembalikan nilai boolean (true jika fungsi search internal mengembalikan node, atau false jika mengembalikan nullptr).

- remove(int value): Memulai proses penghapusan rekursif dari root.

- printInorder(), printPreorder(), printPostorder(): Mencetak teks pengantar lalu menjalankan fungsi rekursi traversal dari root.

Code:

#include <iostream>

struct Node {
int value;
Node* left;
Node* right;

explicit Node(int val) : value(val), left(nullptr), right(nullptr) {}
};

class BST {
private:
Node* root;

Node* insert(Node* node, int value) {
if (node == nullptr) {
return new Node(value);
}

if (value < node->value) {
node->left = insert(node->left, value);
} else if (value > node->value) {
node->right = insert(node->right, value);
}
return node;
}

Node* search(Node* node, int value) const {
if (node == nullptr || node->value == value) {
return node;
}

if (value < node->value) {
return search(node->left, value);
}

return search(node->right, value);
}

Node* findMin(Node* node) const {
Node* current = node;
while (current && current->left != nullptr) {
current = current->left;
}
return current;
}

Node* remove(Node* node, int value) {
if (node == nullptr) {
return nullptr;
}

if (value < node->value) {
node->left = remove(node->left, value);
} else if (value > node->value) {
node->right = remove(node->right, value);
} else {
if (node->left == nullptr) {
Node* temp = node->right;
delete node;
return temp;
} else if (node->right == nullptr) {
Node* temp = node->left;
delete node;
return temp;
}

Node* temp = findMin(node->right);
node->value = temp->value;
node->right = remove(node->right, temp->value);
}
return node;
}

void inorder(Node* node) const {
if (node != nullptr) {
inorder(node->left);
std::cout << node->value << " ";
inorder(node->right);
}
}

void preorder(Node* node) const {
if (node != nullptr) {
std::cout << node->value << " ";
preorder(node->left);
preorder(node->right);
}
}

void postorder(Node* node) const {
if (node != nullptr) {
postorder(node->left);
postorder(node->right);
std::cout << node->value << " ";
}
}

public:
BST() : root(nullptr) {}

void insert(int value) {
root = insert(root, value);
}

bool search(int value) const {
return search(root, value) != nullptr;
}

void remove(int value) {
root = remove(root, value);
}

void printInorder() const {
std::cout << "Inorder : ";
inorder(root);
std::cout << "\n";
}

void printPreorder() const {
std::cout << "Preorder : ";
preorder(root);
std::cout << "\n";
}

void printPostorder() const {
std::cout << "Postorder : ";
postorder(root);
std::cout << "\n";
}
};

int main() {
BST tree;

int valuesToInsert[] = {50, 30, 70, 20, 40, 60, 80};
for (int val : valuesToInsert) {
tree.insert(val);
}

tree.printInorder();
tree.printPreorder();
tree.printPostorder();

std::cout << "\n";
int target = 60;
if (tree.search(target)) {
std::cout << "Key " << target << " found in BST.\n";
} else {
std::cout << "Key " << target << " NOT found in BST.\n";
}

std::cout << "Removing 50...\n";
tree.remove(50);
tree.printInorder();

return 0;
}

Hasil/Output Program:

Penjelasan Program:

Baris Kode

Penjelasan

#include <iostream>

Mengimpor library standar C++ untuk input-output (agar bisa menggunakan std::cout).

struct Node {

Memulai definisi struct bernama Node sebagai representasi satu titik/elemen di pohon.

int value;

Variabel bertipe integer untuk menyimpan data atau angka pada titik tersebut.

Node* left;

Pointer yang akan menunjuk ke anak (cabang) di sebelah kiri.

Node* right;

Pointer yang akan menunjuk ke anak (cabang) di sebelah kanan.

explicit Node(int val) : value(val), left(nullptr), right(nullptr) {}

Constructor untuk menginisialisasi Node baru; mengisi nilai dengan val dan menjadikan pointer anak kiri & kanan kosong (nullptr).

};

Menutup definisi struct Node.

class BST {

Memulai definisi kelas utama BST (Binary Search Tree).

private:

Bagian private: fungsi dan variabel di bawah ini hanya bisa diakses dari dalam kelas BST.

Node* root;

Pointer root menunjuk ke puncak (akar) dari seluruh struktur pohon biner.

Node* insert(Node* node, int value) {

Fungsi internal/rekursif untuk mencari posisi dan menyisipkan nilai baru.

if (node == nullptr) {

Jika mencapai titik kosong atau ujung daun (leaf)...

return new Node(value);

...buat Node baru dengan nilai tersebut dan kembalikan posisinya.

}

Menutup kondisi titik kosong.

if (value < node->value) {

Jika nilai baru lebih kecil dari nilai node saat ini...

node->left = insert(node->left, value);

...maka panggil lagi fungsi ini untuk turun ke cabang sebelah kiri.

} else if (value > node->value) {

Sebaliknya, jika nilai baru lebih besar dari nilai node saat ini...

node->right = insert(node->right, value);

...maka panggil lagi fungsi ini untuk turun ke cabang sebelah kanan.

}

Menutup percabangan kondisi lebih kecil/besar.

return node;

Kembalikan pointer node saat ini agar struktur cabang pohon tetap tersambung ke atas.

}

Menutup fungsi insert internal.

Node* search(Node* node, int value) const {

Fungsi internal/rekursif untuk mencari posisi dari sebuah angka di pohon.

if (node == nullptr || node->value == value) {

Jika node tidak ada (pencarian buntu) ATAU angka pada node cocok dengan yang dicari...

return node;

...kembalikan node tersebut (bisa berupa nullptr atau pointer ke node yang benar).

}

Menutup kondisi awal pencarian.

if (value < node->value) {

Jika nilai yang dicari lebih kecil dari nilai node saat ini...

return search(node->left, value);

...teruskan proses pencarian ke cabang sebelah kiri.

}

Menutup kondisi lebih kecil.

return search(node->right, value);

Jika lebih besar, teruskan proses pencarian ke cabang sebelah kanan.

}

Menutup fungsi search internal.

Node* findMin(Node* node) const {

Fungsi mencari node dengan angka paling kecil di suatu cabang (Inorder Successor).

Node* current = node;

Buat pointer bantu bernama current, mulai dari posisi node yang diberikan.

while (current && current->left != nullptr) {

Selama node saat ini valid dan masih ada anak di sebelah kirinya...

current = current->left;

...terus geser pointer current ke ujung paling kiri.

}

Menutup looping while.

return current;

Kembalikan node paling kiri (terkecil) yang berhasil ditemukan.

}

Menutup fungsi findMin.

Node* remove(Node* node, int value) {

Fungsi internal/rekursif untuk menghapus sebuah node berdasarkan nilainya.

if (node == nullptr) {

Jika sampai pada node kosong (angka tidak ditemukan dalam pohon)...

return nullptr;

...batalkan penghapusan dan kembalikan nullptr.

}

Menutup kondisi node tidak ditemukan.

if (value < node->value) {

Jika angka yang ingin dihapus lebih kecil dari nilai saat ini...

node->left = remove(node->left, value);

...teruskan operasi penghapusan ke cabang sebelah kiri.

} else if (value > node->value) {

Jika angka yang ingin dihapus lebih besar dari nilai saat ini...

node->right = remove(node->right, value);

...teruskan operasi penghapusan ke cabang sebelah kanan.

} else {

Jika nilainya SAMA (inilah node yang mau kita hapus)...

if (node->left == nullptr) {

Kasus 1 & 2: Jika node ini tidak punya anak di sebelah kiri...

Node* temp = node->right;

...simpan anak kanannya di variabel sementara (temp).

delete node;

...hapus node dari memori.

return temp;

...kembalikan anak kanannya agar menyambung dengan posisi node yang dihapus tadi.

} else if (node->right == nullptr) {

Kasus 1 & 2: Jika node ini tidak punya anak di sebelah kanan...

Node* temp = node->left;

...simpan anak kirinya di variabel sementara (temp).

delete node;

...hapus node dari memori.

return temp;

...kembalikan anak kirinya ke atas agar menyambung.

}

Menutup pengecekan kondisi 0 atau 1 anak.

Node* temp = findMin(node->right);

Kasus 3: Punya 2 anak. Cari nilai terkecil di cabang kanan (Inorder Successor).

node->value = temp->value;

Timpa/ganti angka di node saat ini dengan angka dari sang successor.

node->right = remove(node->right, temp->value);

Hapus successor dari posisi aslinya di cabang kanan karena nilainya sudah dipindahkan.

}

Menutup blok ketika node penghapusan ditemukan.

return node;

Kembalikan pointer node untuk menjaga keutuhan struktur pohon.

}

Menutup fungsi remove internal.

void inorder(Node* node) const {

Fungsi internal untuk mencetak secara Inorder (Kiri - Tengah - Kanan).

if (node != nullptr) {

Pastikan node tidak kosong sebelum memproses.

inorder(node->left);

Lakukan proses cetak pada anak sebelah kiri terlebih dahulu.

std::cout << node->value << " ";

Cetak nilai (angka) yang ada di node pusat (Tengah).

inorder(node->right);

Lakukan proses cetak pada anak sebelah kanan terakhir.

}

Menutup kondisi if.

}

Menutup fungsi inorder.

void preorder(Node* node) const {

Fungsi internal untuk mencetak secara Preorder (Tengah - Kiri - Kanan).

if (node != nullptr) {

Pastikan node tidak kosong sebelum memproses.

std::cout << node->value << " ";

Cetak nilai (angka) di node pusat paling pertama (Tengah).

preorder(node->left);

Lanjutkan proses mencetak pada anak kiri.

preorder(node->right);

Lanjutkan proses mencetak pada anak kanan.

}

Menutup kondisi if.

}

Menutup fungsi preorder.

void postorder(Node* node) const {

Fungsi internal untuk mencetak secara Postorder (Kiri - Kanan - Tengah).

if (node != nullptr) {

Pastikan node tidak kosong sebelum memproses.

postorder(node->left);

Selesaikan pencetakan seluruh anak di cabang kiri.

postorder(node->right);

Selesaikan pencetakan seluruh anak di cabang kanan.

std::cout << node->value << " ";

Terakhir, cetak nilai pada node pusatnya (Tengah).

}

Menutup kondisi if.

}

Menutup fungsi postorder.

public:

Bagian public: fungsi-fungsi di bawah ini bisa dipanggil dari blok main.

BST() : root(nullptr) {}

Constructor untuk kelas BST. Memastikan pohon yang baru dibuat kondisinya kosong (root disetel ke nullptr).

void insert(int value) {

Fungsi eksternal sebagai pintu masuk untuk menambahkan data baru.

root = insert(root, value);

Memanggil fungsi internal insert dan hasilnya memperbarui pointer root.

}

Menutup fungsi insert public.

bool search(int value) const {

Fungsi eksternal untuk melakukan pencarian angka.

return search(root, value) != nullptr;

Mengembalikan nilai true jika fungsi internal tidak menghasilkan nullptr (berarti berhasil ditemukan).

}

Menutup fungsi search public.

void remove(int value) {

Fungsi eksternal sebagai pintu masuk untuk menghapus angka.

root = remove(root, value);

Memanggil fungsi internal remove dari akar dan memperbarui pointer root.

}

Menutup fungsi remove public.

void printInorder() const {

Fungsi eksternal untuk memulai proses Inorder traversal.

std::cout << "Inorder : ";

Menampilkan label "Inorder : ".

inorder(root);

Memanggil fungsi internal inorder dimulai dari akar.

std::cout << "\n";

Memberikan ganti baris (Enter) setelah angka selesai dicetak.

}

Menutup fungsi printInorder.

void printPreorder() const {

Fungsi eksternal untuk memulai proses Preorder traversal.

std::cout << "Preorder : ";

Menampilkan label "Preorder : ".

preorder(root);

Memanggil fungsi internal preorder dimulai dari akar.

std::cout << "\n";

Memberikan ganti baris (Enter) setelah angka selesai dicetak.

}

Menutup fungsi printPreorder.

void printPostorder() const {

Fungsi eksternal untuk memulai proses Postorder traversal.

std::cout << "Postorder : ";

Menampilkan label "Postorder : ".

postorder(root);

Memanggil fungsi internal postorder dimulai dari akar.

std::cout << "\n";

Memberikan ganti baris (Enter) setelah angka selesai dicetak.

}

Menutup fungsi printPostorder.

};

Menutup definisi kelas BST.

int main() {

Titik awal jalannya program.

BST tree;

Membentuk satu wujud dari struktur pohon ke memori, yang dinamakan tree.

int valuesToInsert[] = {50, 30, 70, 20, 40, 60, 80};

Menyiapkan kumpulan data (array) angka yang akan di-input ke pohon.

for (int val : valuesToInsert) {

Menggunakan looping otomatis pada array untuk mengambil satu per satu nilainya.

tree.insert(val);

Menyisipkan nilai (val) ke dalam objek tree.

}

Menutup perulangan pengisian.

tree.printInorder();

Memanggil pencetakan Inorder ke layar.

tree.printPreorder();

Memanggil pencetakan Preorder ke layar.

tree.printPostorder();

Memanggil pencetakan Postorder ke layar.

std::cout << "\n";

Membuat satu spasi kosong antar paragraf di hasil output.

int target = 60;

Menyiapkan nilai uji coba pencarian yaitu angka 60.

if (tree.search(target)) {

Jika fungsi pencarian berhasil menemukan target (mengembalikan true)...

std::cout << "Key " << target << " found in BST.\n";

...cetak pesan pemberitahuan sukses ditemukan.

} else {

Jika fungsi mengembalikan false (tidak ditemukan)...

std::cout << "Key " << target << " NOT found in BST.\n";

...cetak pesan gagal ditemukan.

}

Menutup pengecekan Search.

std::cout << "Removing 50...\n";

Mencetak teks pemberitahuan sebelum operasi penghapusan data.

tree.remove(50);

Menghapus angka 50 dari BST (yang kebetulan merupakan root / kasus terkompleks).

tree.printInorder();

Mencetak kembali susunan Inorder untuk melihat pohon setelah angka 50 hilang.

return 0;

Fungsi main selesai dan berhasil dijalankan tanpa ada error.

}

Menutup batas fungsi main.


Komentar

Postingan populer dari blog ini

Pertemuan 11 - Studi Kasus 1 | Sistem Folder Komputer (General Tree)