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.
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)) # FalseSammenligning 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 = 32Dubletter 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)) # 4Hurtig 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.
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
- BST-insert og -søgning
- BST-delete: tre tilfælde
- Validering af BST og in-order-egenskaber
- K-te mindste, intervalsummmer og BST til sorteret array