0Pricing
Coding Interview Prep · Pelajaran

Memvalidasi BST dan Sifat In-Order

Validasi pohon biner sebagai BST menggunakan batas minimum/maksimum yang diteruskan ke bawah pohon dan dengan memeriksa apakah traversal in-order menghasilkan urutan terurut.

Memvalidasi BST dan Sifat In-Order adalah pelajaran Coding Interview Prep gratis di CoddyKit. Ini adalah pelajaran 3 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.

Masalah Validasi BST

Validasi BST (LeetCode #98) adalah masalah wawancara klasik yang menjebak banyak kandidat. Pendekatan naif hanya memeriksa bahwa nilai setiap simpul lebih besar daripada anak kirinya dan lebih kecil daripada anak kanannya, tetapi pemeriksaan lokal ini tidak memadai. Sebuah simpul dalam subpohon mungkin memenuhi aturan lokal, tetapi tetap melanggar sifat BST secara keseluruhan. Solusi yang benar meneruskan batas minimum/maksimum yang valid ke seluruh pohon.

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

# Why local check fails:
#     5
#    / \
#   1   4
#      / \
#     3   6
# Node 4's children (3, 6) satisfy local rule,
# but 4 < 5 and is in the RIGHT subtree -- BST violated!
print('Local check is insufficient -- use min/max bounds')

Pendekatan Batas Minimum/Maksimum

Teruskan batas bawah dan atas ke dalam rekursi. Pada setiap simpul, pastikan bahwa low < node.val < high. Saat melakukan rekursi ke kiri, perbarui batas atas menjadi node.val (subpohon kiri harus lebih kecil). Saat melakukan rekursi ke kanan, perbarui batas bawah menjadi node.val (subpohon kanan harus lebih besar). Mulailah dengan low = -infinity dan high = +infinity.

def is_valid_bst(root, low=float('-inf'), high=float('inf')):
    if not root:
        return True
    if not (low < root.val < high):
        return False
    return (is_valid_bst(root.left, low, root.val) and
            is_valid_bst(root.right, root.val, high))

# Valid BST:
valid = TreeNode(5)
valid.left = TreeNode(3)
valid.right = TreeNode(7)
print(is_valid_bst(valid))  # True

# Invalid BST (3 is in wrong subtree conceptually):
invalid = TreeNode(5)
invalid.left = TreeNode(1)
invalid.right = TreeNode(4)
invalid.right.left = TreeNode(3)
invalid.right.right = TreeNode(6)
print(is_valid_bst(invalid))  # False (4 < 5 in right subtree)

Validasi dengan Penelusuran Urutan-Tengah

Pendekatan validasi alternatif menggunakan sifat terurut pada penelusuran urutan-tengah BST: kumpulkan urutan urutan-tengah dan pastikan urutan tersebut meningkat secara ketat. Cara ini elegan dan mudah dipahami. Namun, cara ini menggunakan ruang tambahan O(n) untuk menyimpan urutan tersebut. Versi yang dioptimalkan menggunakan satu penunjuk prev selama penelusuran untuk memeriksa setiap pasangan tanpa menyimpan seluruh urutan.

def is_valid_bst_inorder(root):
    prev = [float('-inf')]

    def inorder(node):
        if not node:
            return True
        if not inorder(node.left):
            return False
        if node.val <= prev[0]:  # not strictly increasing
            return False
        prev[0] = node.val
        return inorder(node.right)

    return inorder(root)

valid = TreeNode(5)
valid.left = TreeNode(3)
valid.right = TreeNode(7)
valid.left.left = TreeNode(1)
valid.left.right = TreeNode(4)
print(is_valid_bst_inorder(valid))   # True

invalid = TreeNode(5)
invalid.left = TreeNode(6)  # 6 > 5 in left subtree!
print(is_valid_bst_inorder(invalid)) # False

Membandingkan Kedua Pendekatan Validasi

Pendekatan batas minimum/maksimum memerlukan waktu O(n) dan ruang O(h) (hanya batas pada tumpukan pemanggilan). Pendekatan penunjuk sebelumnya pada urutan-tengah juga memerlukan waktu O(n) dan ruang O(h). Keduanya optimal. Pendekatan minimum/maksimum lebih umum dan tetap mudah digunakan saat diperluas ke masalah dengan batasan tambahan. Dalam wawancara, bersiaplah untuk mempresentasikan keduanya dan membahas komprominya—menunjukkan pemahaman terhadap alternatif merupakan indikasi yang kuat.

# Both approaches:
# Time: O(n) -- visit each node once
# Space: O(h) -- call stack depth
# h = O(log n) balanced, O(n) skewed

# When to choose which:
# min/max bounds:
#   - Cleaner for trees with constraints beyond BST
#   - No global state (purely functional)
# in-order prev:
#   - More intuitive (sorted sequence check)
#   - Easier to convert to iterative with a stack

print('Both O(n) time, O(h) space -- choose by clarity')

Memulihkan BST: Dua Simpul yang Tertukar

Memulihkan BST (LeetCode #99) memperbaiki BST yang tepat dua simpulnya tertukar. Selama penelusuran urutan-tengah, BST yang tersusun dengan benar menghasilkan urutan terurut. Jika dua simpul tertukar, akan ada satu atau dua pelanggaran saat prev.val > current.val. Simpul pertama dari pelanggaran pertama dan simpul kedua dari pelanggaran terakhir adalah dua simpul yang salah tempat—tukarkan nilai keduanya.

def recover_tree(root):
    first = second = prev = None

    def inorder(node):
        nonlocal first, second, prev
        if not node:
            return
        inorder(node.left)
        if prev and prev.val > node.val:
            if not first:
                first = prev    # first violator
            second = node       # always update second
        prev = node
        inorder(node.right)

    inorder(root)
    # Swap values of the two misplaced nodes
    if first and second:
        first.val, second.val = second.val, first.val

root = TreeNode(3)
root.left = TreeNode(1)
root.right = TreeNode(4)
root.right.left = TreeNode(2)  # 2 and 3 are swapped
recover_tree(root)
print(root.val, root.right.left.val)  # 2, 3 (fixed)

BST ke Larik Terurut dengan Urutan-Tengah

Mengonversi BST menjadi larik terurut sangat mudah: lakukan penelusuran urutan-tengah dan kumpulkan nilainya. Operasi dengan waktu O(n) dan ruang O(n) ini merupakan cara cepat untuk menerapkan algoritma larik terurut (pencarian biner, dua penunjuk) pada data BST. Operasi ini sering menjadi langkah awal dalam soal BST bertahap seperti 'gabungkan dua BST' atau 'temukan median BST'.

def bst_to_sorted_array(root):
    result = []
    def inorder(node):
        if not node:
            return
        inorder(node.left)
        result.append(node.val)
        inorder(node.right)
    inorder(root)
    return result

root = TreeNode(4)
root.left = TreeNode(2)
root.right = TreeNode(6)
root.left.left = TreeNode(1)
root.left.right = TreeNode(3)
root.right.left = TreeNode(5)
root.right.right = TreeNode(7)
print(bst_to_sorted_array(root))  # [1, 2, 3, 4, 5, 6, 7]

Menggabungkan Dua BST

Untuk menggabungkan dua BST menjadi satu larik terurut, konversikan masing-masing BST menjadi larik terurut dalam waktu O(n) dan O(m), lalu gabungkan kedua larik terurut menggunakan tahap penggabungan dari pengurutan gabung dalam waktu O(n+m). Total waktu: O(n+m). Jika hasilnya perlu berupa BST yang seimbang, masukkan larik terurut hasil penggabungan ke algoritma larik-terurut-ke-BST. Penguraian menjadi submasalah sederhana seperti ini merupakan ciri solusi yang jelas dan mudah dipahami pewawancara.

def merge_two_bsts(root1, root2):
    def inorder(node, arr):
        if not node:
            return
        inorder(node.left, arr)
        arr.append(node.val)
        inorder(node.right, arr)

    arr1, arr2 = [], []
    inorder(root1, arr1)
    inorder(root2, arr2)

    # Merge two sorted arrays
    merged = []
    i = j = 0
    while i < len(arr1) and j < len(arr2):
        if arr1[i] <= arr2[j]:
            merged.append(arr1[i]); i += 1
        else:
            merged.append(arr2[j]); j += 1
    merged.extend(arr1[i:])
    merged.extend(arr2[j:])
    return merged

r1 = TreeNode(2); r1.left = TreeNode(1); r1.right = TreeNode(4)
r2 = TreeNode(3); r2.left = TreeNode(0); r2.right = TreeNode(5)
print(merge_two_bsts(r1, r2))  # [0, 1, 2, 3, 4, 5]

Menghitung Simpul dalam Rentang BST

Hitung jumlah simpul yang nilainya berada dalam rentang [low, high]. Pemindaian urutan-tengah secara menyeluruh memerlukan O(n). Versi yang memanfaatkan BST melakukan pemangkasan: jika nilai simpul saat ini lebih kecil daripada low, tidak ada gunanya memeriksa subpohon kiri (semua nilai di sana juga lebih kecil daripada low). Demikian pula, pangkas subpohon kanan ketika nilai saat ini lebih besar daripada high. Kasus rata-ratanya adalah O(log n + k), dengan k sebagai jumlah simpul yang cocok.

def range_sum_bst(root, low, high):
    if not root:
        return 0
    total = 0
    if low <= root.val <= high:
        total += root.val
    if root.val > low:   # left subtree may have values >= low
        total += range_sum_bst(root.left, low, high)
    if root.val < high:  # right subtree may have values <= high
        total += range_sum_bst(root.right, low, high)
    return total

root = TreeNode(10)
root.left = TreeNode(5)
root.right = TreeNode(15)
root.left.left = TreeNode(3)
root.left.right = TreeNode(7)
root.right.right = TreeNode(18)
print(range_sum_bst(root, 7, 15))  # 7 + 10 + 15 = 32

Nilai Duplikat dan BST Ketat vs Tidak Ketat

Invarian BST standar menggunakan ketidaksamaan ketat: nilai subpohon kiri harus lebih kecil secara ketat dan nilai subpohon kanan harus lebih besar secara ketat. Beberapa soal mengizinkan duplikat, dengan menempatkannya di subpohon kiri (kiri <= akar) atau subpohon kanan (akar < kanan). Saat memvalidasi BST, selalu periksa definisi yang digunakan dalam pernyataan soal. Pendekatan batas minimum/maksimum menangani kedua variasi dengan menyesuaikan apakah pemeriksaan batas harus ketat atau inklusif.

# Strict BST (LeetCode default): left < root < right
def is_valid_strict(root, lo=float('-inf'), hi=float('inf')):
    if not root:
        return True
    if not (lo < root.val < hi):  # STRICT inequalities
        return False
    return (is_valid_strict(root.left, lo, root.val) and
            is_valid_strict(root.right, root.val, hi))

# Non-strict BST (allows duplicates in right): left <= root < right
def is_valid_nonstrict(root, lo=float('-inf'), hi=float('inf')):
    if not root:
        return True
    if not (lo <= root.val < hi):  # NOTE: <= for left side
        return False
    return (is_valid_nonstrict(root.left, lo, root.val + 1) and
            is_valid_nonstrict(root.right, root.val, hi))

print('Always clarify strict vs non-strict with interviewer')

Urutan-Tengah sebagai Alat BST Serbaguna

Penelusuran urutan-tengah adalah alat serbaguna untuk soal BST. Setiap kali soal BST menanyakan urutan terurut, elemen ke-k, kueri rentang, atau sifat urutan, pertimbangkan apakah pemindaian urutan-tengah (atau versi terbaliknya) dapat memberikan jawabannya. Sebagian besar soal khusus BST dapat diringkas menjadi: telusuri dalam urutan terurut dan lakukan sesuatu pada setiap langkah. Mengenali pemetaan ini dengan cepat merupakan keterampilan penting dalam wawancara.

# Problems solved elegantly with in-order:
# 1. Validate BST: check prev <= curr during in-order
# 2. Kth smallest: count k steps in in-order
# 3. Kth largest: count k steps in REVERSE in-order
# 4. Closest value to target: find crossover in in-order
# 5. BST to sorted array: collect in-order into list
# 6. Recover BST: find 1-2 violations in in-order
# 7. Sum of range [lo, hi]: accumulate during in-order

# The key insight: in-order visits BST nodes in sorted order.
# All sorted-order reasoning translates to in-order DFS.
print('In-order = sorted access = foundation of BST reasoning')

Nilai Terdekat dalam BST

Temukan simpul yang nilainya paling dekat dengan suatu sasaran. Manfaatkan pengurutan BST: mulai dari akar, lacak nilai terdekat yang telah ditemukan, lalu bergerak menuju sasaran (ke kiri jika sasaran lebih kecil, ke kanan jika lebih besar). Pendekatan O(h) ini lebih efisien daripada pemindaian urutan-tengah dan menunjukkan pemanfaatan efektif sifat BST untuk memangkas ruang pencarian.

def closest_value(root, target):
    closest = root.val
    curr = root
    while curr:
        if abs(curr.val - target) < abs(closest - target):
            closest = curr.val
        if target < curr.val:
            curr = curr.left
        elif target > curr.val:
            curr = curr.right
        else:
            break  # exact match
    return closest

root = TreeNode(4)
root.left = TreeNode(2)
root.right = TreeNode(5)
root.left.left = TreeNode(1)
root.left.right = TreeNode(3)
print(closest_value(root, 3.714286))  # 4

Pemeriksaan Singkat

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

Ringkasan Pelajaran

Dalam pelajaran ini, Anda mempelajari: validasi BST dengan batas minimum/maksimum (menghindari jebakan pemeriksaan lokal), alternatif penunjuk sebelumnya pada urutan-tengah untuk validasi, serta urutan-tengah sebagai alat BST universal untuk jumlah rentang, nilai terdekat, dan operasi penggabungan. Berikutnya, kita menggunakan sifat urutan-tengah BST untuk menemukan elemen terkecil ke-k.

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Memvalidasi BST dan Sifat In-Order” gratis?

Ya — teks lengkap “Memvalidasi BST dan Sifat In-Order” 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 “Memvalidasi BST dan Sifat In-Order”?

Validasi pohon biner sebagai BST menggunakan batas minimum/maksimum yang diteruskan ke bawah pohon dan dengan memeriksa apakah traversal in-order menghasilkan urutan terurut. 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 3 dari 4.

Berapa lama pelajaran “Memvalidasi BST dan Sifat In-Order” 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

  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 Coding Interview Prep