0Pricing
DSA Interview Prep · Pelajaran

Penghapusan BST: Tiga Kasus

Tangani penghapusan daun, penghapusan dengan satu anak, dan penghapusan dengan dua anak menggunakan penerus in-order, lalu implementasikan algoritmanya dari awal.

Penghapusan BST: Tiga Kasus adalah pelajaran DSA Interview Prep gratis di CoddyKit. Ini adalah pelajaran 2 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar DSA Interview Prep, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus DSA Interview Prep mencakup 4 pelajaran total.

Mengapa Penghapusan BST Rumit

Penghapusan BST adalah operasi paling kompleks di antara tiga operasi inti karena penghapusan sebuah simpul harus mempertahankan properti BST untuk seluruh pohon. Ada tiga kasus berbeda bergantung pada anak simpul tersebut: tidak memiliki anak (daun), memiliki satu anak, atau memiliki dua anak. Setiap kasus memerlukan strategi yang berbeda. Pewawancara menyukai masalah ini karena menguji manipulasi penunjuk, kemampuan memikirkan kasus khusus, dan pengetahuan tentang konsep penerus dalam-urutan.

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

# Three cases for deleting a node:
# Case 1: Leaf node (no children) -> simply remove it
# Case 2: One child -> replace node with its child
# Case 3: Two children -> replace value with in-order successor
#          then delete the in-order successor
print('BST delete: 3 cases based on number of children')

Kasus 1: Menghapus Simpul Daun

Simpul daun tidak memiliki anak. Penghapusannya sederhana: kembalikan None dari pemanggilan rekursif, sehingga simpul induk menetapkan penunjuknya (kiri atau kanan) menjadi kosong. Ini adalah kasus dasar yang harus ditangani terlebih dahulu oleh semua implementasi penghapusan BST. Pastikan cara ini berhasil untuk kasus khusus ketika pohon hanya memiliki satu simpul (akarnya adalah daun).

def find_min(node):
    while node.left:
        node = node.left
    return node

# Demonstrating leaf deletion:
root = TreeNode(5)
root.left = TreeNode(3)
root.right = TreeNode(7)
root.left.left = TreeNode(1)  # leaf
root.left.right = TreeNode(4)  # leaf

# To delete node 1 (leaf): set root.left.left = None
root.left.left = None
print(root.left.left)  # None -- deleted
print(root.left.val)   # 3 still intact

Kasus 2: Simpul dengan Satu Anak

Ketika sebuah simpul memiliki tepat satu anak, gantilah simpul tersebut dengan anak itu. Kembalikan anak yang tidak kosong dari pemanggilan rekursif agar penunjuk induknya diperbarui untuk melewati simpul yang dihapus. Hal ini berjalan mulus baik anak tunggal tersebut berada di kiri maupun di kanan—cukup kembalikan yang tersedia.

# Demonstrating one-child deletion:
# Tree:  5
#       / \
#      3   7
#       \   
#        4  
# Delete node 3 (has only right child 4):
# Result: 5
#        / \
#       4   7

root = TreeNode(5)
root.left = TreeNode(3)
root.right = TreeNode(7)
root.left.right = TreeNode(4)

# In the recursive implementation:
# When we reach node 3 and it has no left child,
# we return root.right (node 4) to the parent.
# Parent sets its left pointer to 4, skipping 3.
print('One-child case: return the surviving child')

Kasus 3: Simpul dengan Dua Anak

Ketika sebuah simpul memiliki dua anak, kita tidak dapat menghapusnya begitu saja. Sebagai gantinya, temukan penerus urutan-tengah simpul tersebut (nilai terkecil dalam subpohon kanan), salin nilainya ke simpul saat ini, lalu hapus penerus urutan-tengah dari subpohon kanan. Penerus tersebut memiliki paling banyak satu anak (tidak memiliki anak kiri), sehingga penghapusannya termasuk Kasus 1 atau Kasus 2—yang sudah kita ketahui cara menanganinya.

# Demonstrating two-child deletion:
# Tree:  5
#       / \
#      3   7
#         / \
#        6   9
# Delete node 5 (two children 3 and 7):
# In-order successor = 6 (smallest in right subtree)
# Step 1: replace 5's value with 6
# Step 2: delete 6 from right subtree
# Result:  6
#         / \
#        3   7
#             \
#              9
print('Two-child case: replace with in-order successor')

Implementasi Lengkap Penghapusan BST

Penghapusan rekursif lengkap menggabungkan ketiga kasus tersebut. Temukan simpul yang akan dihapus dengan membandingkan nilai, lalu tangani kasus yang sesuai. Pola mengembalikan akar (yang mungkin telah diubah) pada setiap tingkat dan menetapkannya kembali ke root.left atau root.right menangani semua pembaruan penunjuk secara elegan tanpa perlu melacak induk secara eksplisit. Kompleksitas waktu adalah O(h).

def delete_node(root, key):
    if not root:
        return None  # key not found
    if key < root.val:
        root.left = delete_node(root.left, key)
    elif key > root.val:
        root.right = delete_node(root.right, key)
    else:  # found the node to delete
        if not root.left:   # Case 1 or 2: no left child
            return root.right
        if not root.right:  # Case 2: no right child
            return root.left
        # Case 3: two children -> find in-order successor
        successor = find_min(root.right)
        root.val = successor.val  # copy successor value up
        root.right = delete_node(root.right, successor.val)  # delete successor
    return root

root = TreeNode(5)
root.left = TreeNode(3)
root.right = TreeNode(7)
root.right.left = TreeNode(6)
root.right.right = TreeNode(9)
root = delete_node(root, 5)
print(root.val)  # 6 (successor replaced 5)

Mengapa Menggunakan Penerus Urutan-Tengah?

Penerus urutan-tengah (nilai minimum dari subpohon kanan) digunakan, bukan maksimum dari subpohon kiri, karena keduanya merupakan pilihan yang valid—penggunaan salah satunya tetap mempertahankan sifat BST. Pendahulu urutan-tengah (nilai maksimum dari subpohon kiri) juga dapat digunakan. Beberapa implementasi menggunakannya secara bergantian agar pohon tetap seimbang. Dalam wawancara, versi penerus urutan-tengah lebih sering diharapkan; sebutkan bahwa pendahulu juga sama baiknya.

# Both approaches are valid for two-child deletion:

# Option A: Replace with in-order SUCCESSOR (min of right subtree)
# - Successor goes to current position
# - Delete successor from right subtree

# Option B: Replace with in-order PREDECESSOR (max of left subtree)
# - Predecessor goes to current position
# - Delete predecessor from left subtree

def find_max(node):
    while node.right:
        node = node.right
    return node

# Using predecessor:
def delete_node_pred(root, key):
    if not root:
        return None
    if key < root.val:
        root.left = delete_node_pred(root.left, key)
    elif key > root.val:
        root.right = delete_node_pred(root.right, key)
    else:
        if not root.left:
            return root.right
        if not root.right:
            return root.left
        pred = find_max(root.left)
        root.val = pred.val
        root.left = delete_node_pred(root.left, pred.val)
    return root

print('Both successor and predecessor deletion are correct')

Menghapus Semua Simpul dengan Suatu Nilai

Variasi soal meminta Anda menghapus semua simpul dengan nilai dalam suatu rentang atau yang memenuhi suatu kondisi. Untuk BST, cara ini efisien: lakukan rekursi ke subpohon yang sesuai berdasarkan perbandingan, lalu terapkan operasi penghapusan di setiap tempat kondisi tersebut terpenuhi. Struktur rekursif penghapusan BST secara alami dapat diperluas ke skenario ini tanpa memerlukan tahap penelusuran terpisah.

# Delete all nodes with values outside [low, high]
def trim_bst(root, low, high):
    if not root:
        return None
    if root.val < low:
        # Entire left subtree is also < low, skip to right
        return trim_bst(root.right, low, high)
    if root.val > high:
        # Entire right subtree is also > high, skip to left
        return trim_bst(root.left, low, high)
    # Current node is within range
    root.left = trim_bst(root.left, low, high)
    root.right = trim_bst(root.right, low, high)
    return root

root = TreeNode(3)
root.left = TreeNode(0)
root.right = TreeNode(4)
root.left.right = TreeNode(2)
root.left.right.left = TreeNode(1)
root = trim_bst(root, 1, 3)
print(root.val, root.left.val)  # 3 2

Pola Pengiterasi BST

Pengiterasi BST (LeetCode #173) mengembalikan elemen dalam urutan terurut satu per satu, dengan waktu rata-rata O(1) dan ruang O(h). Implementasikan dengan tumpukan yang menyimulasikan penelusuran urutan-tengah secara iteratif: saat dibuat, dorong semua simpul kiri mulai dari akar. Pada next(), ambil elemen teratas, lalu dorong semua simpul kiri dari subpohon kanan. Ini merupakan penguraian bertahap yang terkendali dari algoritma urutan-tengah iteratif.

class BSTIterator:
    def __init__(self, root):
        self.stack = []
        self._push_left(root)

    def _push_left(self, node):
        while node:
            self.stack.append(node)
            node = node.left

    def next(self):
        node = self.stack.pop()
        if node.right:
            self._push_left(node.right)
        return node.val

    def has_next(self):
        return bool(self.stack)

root = TreeNode(7)
root.left = TreeNode(3)
root.right = TreeNode(15)
root.right.left = TreeNode(9)
it = BSTIterator(root)
while it.has_next():
    print(it.next(), end=' ')  # 3 7 9 15

Penghapusan Simpul: Analisis Kompleksitas

Penghapusan BST berjalan dalam waktu O(h), dengan h sebagai tinggi pohon. Untuk BST yang seimbang, kompleksitasnya O(log n). Untuk pohon yang miring, kompleksitasnya menurun menjadi O(n). Pencarian penerus urutan-tengah menambahkan paling banyak satu penelusuran O(h) ekstra pada subpohon kanan, yang tidak mengubah kompleksitas keseluruhan. Kompleksitas ruang adalah O(h) untuk tumpukan pemanggilan dalam implementasi rekursif.

# Complexity summary for BST operations:
# Operation | Balanced  | Skewed
# ----------|-----------|-------
# Search    | O(log n)  | O(n)
# Insert    | O(log n)  | O(n)
# Delete    | O(log n)  | O(n)
# Min/Max   | O(log n)  | O(n)
# In-order  | O(n)      | O(n)   (visits all nodes)

# The key: BST guarantees these complexities only when balanced.
# Python standard library has no balanced BST.
# Use sortedcontainers.SortedList for O(log n) ops in practice.
print('All BST core ops are O(h): O(log n) balanced, O(n) skewed')

Dua Jumlah dalam BST

Dua Jumlah IV dalam BST menanyakan apakah ada dua simpul yang jumlahnya sama dengan suatu sasaran. Salah satu pendekatan menggunakan himpunan: penelusuran urutan-tengah mengumpulkan nilai sambil memeriksa apakah target - current sudah ada dalam himpunan. Pendekatan yang lebih elegan menggunakan pengiterasi BST maju dan pengiterasi BST mundur secara bersamaan (seperti dua penunjuk)—cara ini menghindari ruang tambahan selain O(h) untuk tumpukan setiap pengiterasi.

def find_target_bst(root, k):
    seen = set()
    def inorder(node):
        if not node:
            return False
        if inorder(node.left):
            return True
        if k - node.val in seen:
            return True
        seen.add(node.val)
        return inorder(node.right)
    return inorder(root)

root = TreeNode(5)
root.left = TreeNode(3)
root.right = TreeNode(6)
root.left.left = TreeNode(2)
root.left.right = TreeNode(4)
root.right.right = TreeNode(7)
print(find_target_bst(root, 9))  # True (2+7)
print(find_target_bst(root, 28)) # False

Mengubah BST menjadi Pohon Jumlah Lebih Besar

Pohon jumlah lebih besar (LeetCode #538) mengganti nilai setiap simpul dengan jumlah semua nilai yang lebih besar dari atau sama dengan nilainya dalam BST. Inti gagasannya adalah melakukan penelusuran urutan-tengah terbalik (kanan → akar → kiri) untuk mengunjungi simpul dalam urutan menurun dan mengakumulasikan jumlah berjalan. Proses ini berjalan dalam waktu O(n) dan ruang O(h).

def bst_to_gst(root):
    acc = [0]  # running accumulated sum

    def reverse_inorder(node):
        if not node:
            return
        reverse_inorder(node.right)   # visit larger values first
        acc[0] += node.val
        node.val = acc[0]             # replace with cumulative sum
        reverse_inorder(node.left)

    reverse_inorder(root)
    return root

root = TreeNode(4)
root.left = TreeNode(1)
root.right = TreeNode(6)
root.right.left = TreeNode(5)
root.right.right = TreeNode(7)
bst_to_gst(root)
print(root.val)       # 4+5+6+7 = 22
print(root.right.val) # 5+6+7 = 18

Pemeriksaan Singkat

Uji pemahaman Anda tentang konsep Struktur Data & Algoritma — Persiapan Wawancara Pengodean dari pelajaran ini.

Ringkasan Pelajaran

Dalam pelajaran ini, Anda mempelajari: tiga kasus penghapusan BST (daun, satu anak, dua anak), teknik penerus urutan-tengah untuk penghapusan simpul dengan dua anak, dan pola rekursif yang rapi seperti pengiterasi BST serta pohon jumlah lebih besar dari BST. Berikutnya, kita memvalidasi kebenaran BST dan memanfaatkan sifat urutan-tengah.

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Penghapusan BST: Tiga Kasus” gratis?

Ya — teks lengkap “Penghapusan BST: Tiga Kasus” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus DSA Interview Prep, upgrade ke CoddyKit PRO. Kursus DSA Interview Prep mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Penghapusan BST: Tiga Kasus”?

Tangani penghapusan daun, penghapusan dengan satu anak, dan penghapusan dengan dua anak menggunakan penerus in-order, lalu implementasikan algoritmanya dari awal. Kamu berlatih DSA Interview Prep dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai DSA Interview Prep?

Tidak diperlukan pengalaman sebelumnya. DSA Interview Prep di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 2 dari 4.

Berapa lama pelajaran “Penghapusan BST: Tiga Kasus” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran DSA Interview Prep ini?

Ya. Setiap pelajaran DSA Interview Prep menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Penyisipan dan Pencarian BST
  2. Penghapusan BST: Tiga Kasus
  3. Memvalidasi BST dan Sifat In-Order
  4. Elemen ke-K Terkecil, Jumlah Rentang, dan BST menjadi Array Terurut
← Kembali ke DSA Interview Prep