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 Coding 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 Coding Interview Prep, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Coding 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 intactKasus 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 2Pola 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 15Penghapusan 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)) # FalseMengubah 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 = 18Pemeriksaan 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 Coding Interview Prep, upgrade ke CoddyKit PRO. Kursus Coding 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 Coding 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 Coding Interview Prep?
Tidak diperlukan pengalaman sebelumnya. Coding 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 Coding Interview Prep ini?
Ya. Setiap pelajaran Coding 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
- Penyisipan dan Pencarian BST
- Penghapusan BST: Tiga Kasus
- Memvalidasi BST dan Sifat In-Order
- Elemen ke-K Terkecil, Jumlah Rentang, dan BST menjadi Array Terurut