DSA Interview Prep · Lektion

Validering af BST og in-order-egenskaber

Validér et binært træ som en BST med min/max-grænser, der føres ned gennem træet, og ved at kontrollere, at in-order-gennemløbet giver en sorteret sekvens.

Lektion 3 af 413 trin

Validering af BST og in-order-egenskaber er en gratis DSA Interview Prep-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i DSA Interview Prep, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. DSA Interview Prep-kurset indeholder 4 lektioner i alt.

Problemet med validering af BST

Valider BST (LeetCode #98) er en klassisk interviewopgave, som volder mange kandidater problemer. Den naive tilgang kontrollerer kun, at hver knudes værdi er større end dens venstre barn og mindre end dens højre barn, men denne lokale kontrol er utilstrækkelig. En knude i et undertræ kan godt opfylde den lokale regel og stadig overtræde den globale BST-egenskab. Den korrekte løsning sender gyldige minimums- og maksimumsgrænser ned gennem træet.

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

Tilgangen med minimums- og maksimumsgrænser

Send nedre og øvre grænser videre gennem rekursionen. Kontrollér ved hver knude, at low < node.val < high. Når du rekursér til venstre, skal du opdatere den øvre grænse til node.val (det venstre undertræ skal være mindre). Når du rekursér til højre, skal du opdatere den nedre grænse til node.val (det højre undertræ skal være større). Start med low = -infinity og 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)

Validering med inorder-gennemløb

En alternativ valideringstilgang bruger BST'ens sorterede inorder-egenskab: indsaml inorder-sekvensen, og kontrollér, at den er strengt voksende. Det er elegant og nemt at forstå. Til gengæld bruger det O(n) ekstra plads til at gemme sekvensen. En optimeret version bruger en enkelt prev-reference under gennemløbet til at kontrollere hvert par uden at gemme hele sekvensen.

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

Sammenligning af de to valideringstilgange

Tilgangen med minimums- og maksimumsgrænser bruger O(n) tid og O(h) plads (kun grænserne på kaldestakken). Tilgangen med inorder-gennemløb og prev-reference bruger også O(n) tid og O(h) plads. Begge er optimale. Tilgangen med minimums- og maksimumsgrænser er mere generel og fungerer rent, når den udvides til problemer med yderligere begrænsninger. Til interviews skal du være klar til at præsentere begge tilgange og diskutere afvejninger — det er et stærkt signal, at du kender alternativerne.

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

Gendan BST: To ombyttede knuder

Gendan BST (LeetCode #99) reparerer en BST, hvor præcis to knuder er blevet ombyttet. Under et inorder-gennemløb giver en korrekt sorteret BST en sorteret sekvens. Hvis to knuder er blevet ombyttet, vil der være én eller to overtrædelser, hvor prev.val > current.val. Den første knude i den første overtrædelse og den anden knude i den sidste overtrædelse er de to forkert placerede knuder — ombyt deres værdier.

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 fra inorder til sorteret array

Det er trivielt at konvertere en BST til et sorteret array: udfør et inorder-gennemløb, og indsaml værdierne. Denne operation, der bruger O(n) tid og O(n) plads, er en hurtig måde at anvende algoritmer til sorterede arrays (binær søgning, to pegere) på BST-data. Den er ofte et første trin i BST-opgaver med flere dele, f.eks. 'flet to BST'er' eller 'find medianen i en 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]

Fletning af to BST'er

Hvis du vil flette to BST'er til ét sorteret array, skal du konvertere hver BST til et sorteret array i henholdsvis O(n) og O(m) og derefter flette de to sorterede arrays ved hjælp af fletningstrinnet i fletningssortering i O(n+m). Den samlede tid er O(n+m). Hvis du skal have resultatet som en balanceret BST, skal du give det flettede sorterede array som input til algoritmen, der konverterer et sorteret array til en BST. Denne opdeling i simple delproblemer er kendetegnende for en klar løsning, som er let for en interviewer at følge.

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]

Tæl knuder i et BST-interval

Tæl, hvor mange knuder der har værdier i intervallet [low, high]. En udtømmende inorder-gennemgang bruger O(n). Den BST-bevidste version beskærer søgningen: Hvis den aktuelle knudes værdi er mindre end low, er der ingen grund til at kontrollere det venstre undertræ (alle værdier dér er også mindre end low). På samme måde kan du springe det højre undertræ over, når den aktuelle værdi er større end high. Den gennemsnitlige kompleksitet er O(log n + k), hvor k er antallet af knuder, der matcher.

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

Dubletter og strenge kontra ikke-strenge BST'er

BST'ens standardinvariant bruger streng ulighed: værdierne i det venstre undertræ er strengt mindre, og værdierne i det højre undertræ er strengt større. Nogle opgaver tillader dubletter og placerer dem i det venstre undertræ (left <= root) eller det højre undertræ (root < right). Når du validerer BST'er, skal du altid kontrollere opgavens definition. Tilgangen med minimums- og maksimumsgrænser håndterer begge varianter ved at justere, om grænsekontrollen skal være streng eller inklusiv.

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

Inorder som universelt BST-værktøj

Inorder-gennemløbet er schweizerkniven blandt BST-værktøjer. Når en BST-opgave handler om sorteret rækkefølge, det k'te element, intervalforespørgsler eller sekvensegenskaber, skal du overveje, om et inorder-gennemløb (eller det omvendte) giver dig svaret. De fleste BST-specifikke opgaver kan reduceres til: gennemgå i sorteret rækkefølge, og gør noget ved hvert trin. At genkende denne sammenhæng hurtigt er en vigtig interviewfærdighed.

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

Nærmeste værdi i en BST

Find den knude, hvis værdi ligger tættest på en given målværdi. Udnyt BST'ens sortering: Start ved roden, hold styr på den hidtil nærmeste værdi, og bevæg dig mod målværdien (gå til venstre, hvis målværdien er mindre, og til højre, hvis den er større). Denne tilgang, der bruger O(h) tid, er mere effektiv end et inorder-gennemløb og viser, hvordan du effektivt kan bruge BST-egenskaben til at beskære søgeområdet.

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

Hurtig kontrol

Afprøv din forståelse af begreberne fra Data Structures & Algorithms — Coding Interview Prep i denne lektion.

Opsummering af lektionen

I denne lektion lærte du om: validering af BST med minimums- og maksimumsgrænser (så du undgår fejlen med kun lokal kontrol), det alternative inorder-gennemløb med prev-reference til validering samt inorder som det universelle BST-værktøj til intervalsomme, nærmeste værdi og fletteoperationer. Næste trin er at bruge BST'ens inorder-egenskaber til at finde det k'te mindste element.

Gratis at komme i gang

Lær Python med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
30
Lektioner
120

Ofte stillede spørgsmål

Er lektionen “Validering af BST og in-order-egenskaber” gratis?

Ja — alle 3 lektioner i læringssporet DSA Interview Prep, inklusive “Validering af BST og in-order-egenskaber”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. DSA Interview Prep-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Validering af BST og in-order-egenskaber”?

Validér et binært træ som en BST med min/max-grænser, der føres ned gennem træet, og ved at kontrollere, at in-order-gennemløbet giver en sorteret sekvens. Du øver dig i DSA Interview Prep med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på DSA Interview Prep?

Der kræves ingen tidligere erfaring. DSA Interview Prep på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.

Hvor lang tid tager lektionen “Validering af BST og in-order-egenskaber”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne DSA Interview Prep-lektion?

Ja. Alle DSA Interview Prep-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. BST-insert og -søgning
  2. BST-delete: tre tilfælde
  3. Validering af BST og in-order-egenskaber
  4. K-te mindste, intervalsummmer og BST til sorteret array
← Tilbage til DSA Interview Prep