DSA Interview Prep · Oppitunti

BST:n kelvollisuuden ja in-order-ominaisuuksien tarkistaminen

Varmistakaa, että binääripuu on BST, välittämällä minimi- ja maksimiarvorajat puussa alaspäin sekä tarkistamalla, että in-order-läpikäynti tuottaa järjestetyn jonon.

Oppitunti 3/413 vaihetta

BST:n kelvollisuuden ja in-order-ominaisuuksien tarkistaminen on ilmainen DSA Interview Prep-oppitunti CoddyKitissä. Tämä on oppitunti 3/4. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu DSA Interview Prep-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. DSA Interview Prep-kurssilla on yhteensä 4 oppituntia.

BST:n validointiongelma

Validate BST (LeetCode #98) on klassinen haastattelutehtävä, joka aiheuttaa monille ehdokkaille vaikeuksia. Naiivissa lähestymistavassa tarkistetaan vain, että kunkin solmun arvo on suurempi kuin sen vasemman lapsen arvo ja pienempi kuin oikean lapsen arvo, mutta tämä paikallinen tarkistus ei riitä. Alipuun solmu voi täyttää paikallisen säännön ja silti rikkoa BST:n yleistä ominaisuutta. Oikea ratkaisu välittää puuhun alaspäin kelvolliset minimi- ja maksimirajat.

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')

Minimi- ja maksimirajoihin perustuva lähestymistapa

Välittäkää ala- ja ylärajat rekursion mukana alaspäin. Tarkistakaa jokaisessa solmussa, että low < node.val < high. Kun siirrytte vasemmalle, päivittäkää ylärajaksi node.val (vasemman alipuun arvojen on oltava pienempiä). Kun siirrytte oikealle, päivittäkää alarajaksi node.val (oikean alipuun arvojen on oltava suurempia). Aloittakaa arvoilla low = -infinity ja 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)

Validointi in-order-läpikäynnillä

Vaihtoehtoisessa validointitavassa hyödynnetään BST:n in-order-järjestyksen lajitteluominaisuutta: kerätkää in-order-jono ja tarkistakaa, että se on aidosti kasvava. Tämä on elegantti ja helposti perusteltava ratkaisu. Se käyttää kuitenkin O(n) ylimääräistä tilaa jonon tallentamiseen. Optimoidussa versiossa läpikäynnin aikana käytetään yhtä prev-osoitinta, jolloin kukin pari voidaan tarkistaa tallentamatta koko jonoa.

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

Molempien validointitapojen vertailu

Minimi- ja maksimirajoihin perustuva lähestymistapa käyttää aikaa O(n) ja tilaa O(h) (vain kutsupinon rajat). In-order-läpikäynnin prev-osoitinta käyttävä lähestymistapa käyttää niin ikään aikaa O(n) ja tilaa O(h). Molemmat ovat optimaalisia. Minimi- ja maksimirajoihin perustuva lähestymistapa on yleiskäyttöisempi ja toimii selkeästi myös silloin, kun sitä laajennetaan lisärajoitteita sisältäviin ongelmiin. Haastatteluissa kannattaa varautua esittelemään molemmat ja keskustelemaan niiden kompromisseista — vaihtoehtojen tuntemuksen osoittaminen antaa vahvan kuvan osaamisesta.

# 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')

BST:n korjaaminen: kaksi vaihdettua solmua

Recover BST (LeetCode #99) korjaa BST:n, jossa täsmälleen kaksi solmua on vaihtanut paikkaa. Oikein järjestetty BST tuottaa in-order-läpikäynnissä lajitellun jonon. Jos kaksi solmua on vaihdettu, jonossa on yksi tai kaksi rikkomusta kohdissa, joissa prev.val > current.val. Ensimmäisen rikkomuksen ensimmäinen solmu ja viimeisen rikkomuksen toinen solmu ovat väärissä paikoissa olevat solmut — vaihtakaa niiden arvot keskenään.

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:n muuntaminen lajitelluksi taulukoksi in-order-läpikäynnillä

BST:n muuntaminen lajitelluksi taulukoksi on yksinkertaista: tehkää in-order-läpikäynti ja kerätkää arvot. Tämä aikaa O(n) ja tilaa O(n) käyttävä toiminto on nopea tapa hyödyntää lajiteltujen taulukoiden algoritmeja (binäärihakua ja kahta osoitinta) BST:n datan käsittelyssä. Se toimii usein välietappina moniosaisissa BST-tehtävissä, kuten tehtävissä ”merge two BSTs” tai ”find median of 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]

Kahden BST:n yhdistäminen

Kun kaksi BST:tä yhdistetään yhdeksi lajitelluksi taulukoksi, muuntakaa kumpikin ensin lajitelluksi taulukoksi ajassa O(n) ja O(m), minkä jälkeen yhdistäkää lajitellut taulukot lomituslajittelun lomitusvaiheella ajassa O(n+m). Kokonaisaikavaativuus on O(n+m). Jos tarvitsette tuloksen tasapainotettuna BST:nä, antakaa yhdistetty lajiteltu taulukko lajitellun taulukon BST:ksi muuntavalle algoritmille. Tämä ongelman jakaminen yksinkertaisiksi osaongelmiksi on selkeän ja haastattelutilanteeseen sopivan ratkaisun tunnusmerkki.

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]

BST:n väliin kuuluvien solmujen laskeminen

Laskekaa, kuinka monen solmun arvo kuuluu välille [low, high]. Brute force -tyyppinen in-order-läpikäynti käyttää aikaa O(n). BST:n ominaisuuksia hyödyntävä versio karsii haaroja: jos nykyisen solmun arvo on pienempi kuin low, vasenta alipuuta ei tarvitse tarkistaa, koska kaikki sen arvot ovat niin ikään pienempiä kuin low. Vastaavasti oikea alipuu voidaan karsia, kun nykyinen arvo on suurempi kuin high. Keskimääräinen aikavaativuus on O(log n + k), missä k on hakuehdon täyttävien solmujen määrä.

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

Toistuvat arvot sekä tiukka ja ei-tiukka BST

BST:n vakiomuotoinen invariantti käyttää tiukkaa epäyhtälöä: vasemman alipuun arvojen on oltava aidosti pienempiä ja oikean alipuun arvojen aidosti suurempia. Joissakin tehtävissä sallitaan toistuvat arvot, jotka sijoitetaan vasempaan alipuuhun (left <= root) tai oikeaan alipuuhun (root < right). BST:tä validoitaessa tarkistakaa aina tehtävänannossa annettu määritelmä. Minimi- ja maksimirajoihin perustuva lähestymistapa käsittelee molemmat muunnelmat säätämällä, ovatko rajatarkistukset tiukkoja vai sallivatko ne yhtäsuuruuden.

# 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')

In-order-läpikäynti BST:n yleiskäyttöisenä työkaluna

In-order-läpikäynti on BST-tehtävien monitoimityökalu. Aina kun BST-tehtävässä kysytään lajittelujärjestyksestä, k:nnen alkion löytämisestä, välikyselyistä tai jonon ominaisuuksista, kannattaa harkita, antaako in-order-läpikäynti (tai sen käänteinen versio) vastauksen. Useimmat BST-kohtaiset tehtävät voidaan palauttaa muotoon: käykää solmut läpi lajitellussa järjestyksessä ja tehkää jotakin jokaisessa vaiheessa. Tämän yhteyden tunnistaminen nopeasti on tärkeä haastattelutaito.

# 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')

BST:n lähin arvo

Etsikää solmu, jonka arvo on lähimpänä annettua kohdearvoa. Hyödyntäkää BST:n järjestystä: aloittakaa juuresta, pitäkää kirjaa siihen mennessä nähdystä lähimmästä arvosta ja siirtykää kohteen suuntaan (vasemmalle, jos kohde on pienempi, ja oikealle, jos se on suurempi). Tämä O(h):n lähestymistapa on tehokkaampi kuin in-order-läpikäynti ja osoittaa, miten BST:n ominaisuutta voidaan hyödyntää hakuavaruuden karsimiseen.

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

Pikatesti

Testatkaa, miten hyvin hallitsette tämän oppitunnin Data Structures & Algorithms — Coding Interview Prep -käsitteet.

Oppitunnin yhteenveto

Tässä oppitunnissa opitte: BST:n validoinnin minimi- ja maksimirajojen avulla (paikallisen tarkistuksen ongelman välttäminen), in-order-läpikäynnin prev-osoittimeen perustuvan vaihtoehdon validointiin sekä in-order-läpikäynnin yleiskäyttöisenä BST-työkaluna väliin kuuluvien arvojen summiin, lähimmän arvon etsimiseen ja yhdistämistoimintoihin. Seuraavaksi hyödynnämme BST:n in-order-ominaisuuksia k:nnen pienimmän alkion löytämisessä.

Aloita maksutta

Opi Python tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
30
Oppitunnit
120

Usein kysytyt kysymykset

Onko oppitunti ”BST:n kelvollisuuden ja in-order-ominaisuuksien tarkistaminen” ilmainen?

Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa DSA Interview Prep-oppimispolun 3 oppituntia, myös oppitunnin “BST:n kelvollisuuden ja in-order-ominaisuuksien tarkistaminen”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. DSA Interview Prep-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”BST:n kelvollisuuden ja in-order-ominaisuuksien tarkistaminen”?

Varmistakaa, että binääripuu on BST, välittämällä minimi- ja maksimiarvorajat puussa alaspäin sekä tarkistamalla, että in-order-läpikäynti tuottaa järjestetyn jonon. Harjoittelet DSA Interview Prep-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni DSA Interview Prep-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin DSA Interview Prep-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 3/4.

Kuinka kauan ”BST:n kelvollisuuden ja in-order-ominaisuuksien tarkistaminen”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä DSA Interview Prep-oppitunnilla?

Kyllä. Jokainen DSA Interview Prep-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. BST:n lisäys ja haku
  2. BST:n poisto: kolme tapausta
  3. BST:n kelvollisuuden ja in-order-ominaisuuksien tarkistaminen
  4. K:nneksi pienin, alueen summa ja BST järjestetyksi taulukoksi
← Takaisin: DSA Interview Prep