Binary Search Tree (BST)

Context: FIT1008_MOC · a Binary Tree with an ordering Invariant · an alternative implementation of a Dictionary (ADT) · cost depends on balance

Quick Revision

  • 🎯 Objective: left < node < right invariant ➔ search becomes halving, cost — binary search made dynamic.
  • 📦 Core Components: search ➔ go left/right | insert ➔ return-and-relink | delete ➔ three cases via in-order successor.
  • ⚡ Key Constraint: balanced, degenerate ➔ good at search and insert/delete, and ordered (unlike a hash table).

📝 Core

1. The BST (Ordering Invariant)

  • Invariant ➔ for every node, left-subtree keys less, right-subtree keys greater (keys unique).
  • Search halving ➔ go left/right by comparison ➔ cost depth; key/item kept separate.
  • Balance dependency ➔ balanced , sorted-insert stick ; self-balancing = AVL/red-black/2-3-4.
  • Mechanism ➔ recurse left/right by key; at the empty leaf position, create the node.
  • Critical returnreturn current and re-assign the link on the way back (current.left = insert_aux(...), self.root = insert_aux(...)).
  • Duplicate ➔ raise, or current.item = item for insert-or-update (__setitem__).

3. Delete (Three Cases via Successor)

  • Leaf ➔ null the parent link.
  • One child ➔ bypass (parent points to the child).
  • Two children ➔ replace key/item with the in-order successor (one step right, then all the way left), then delete it (successor has no left child ➔ easy case).

⚙️ Core Implementation

🔹 Search + Insert

🔹 Iterating a tree — explicit-stack preorder

⚖️ Core Decision Matrix

Variant / Strategysearchinsert / deleteOrdered?Cache / Note
BST (balanced)yesgood at both; range/successor
BST (degenerate)yessorted input → LinkList stick
sorted array shiftyesfast search, slow insert
sorted linked list positionedyesfast insert, slow search
Hash Table expected expectednofastest, no order/range

When It Flips: a BST is good at both search and insert/delete (binary search + constant re-link), unlike a sorted array/linked list (each wins one); vs a hash table it is slower per op but traversable in key order with range/predecessor/successor queries. Correctness = ops maintain the BST invariant (BST in ⟹ BST out).

🔹 Building the tree: insertions into an empty BST (FIT2004)

  • Worst case ➔ feed keys already sorted ➔ every key goes to the end of a growing chain ➔ the -th insertion walks nodes ⟹ (Arithmetic Series).
  • Best case ➔ keys arriving in an order that keeps the tree balanced ⟹ each insertion .
  • Per-op worst × is only an UPPER bound ➔ “each insertion is , so of them are ” is valid but does not establish — tightness needs a witness input on which the worst cases genuinely co-occur, which sorted input supplies.

📊 Exam Execution Trace

Manual Execution Trace

Insert 5, 3, 8, 4, 7, then delete 8 (one child 7):

Step / StateTrigger OpTree (parenthesised) Payload
0 (Init)init(empty)
1insert 55
2insert 35(3,_)
3insert 85(3, 8)
4insert 45(3(_,4), 8)
5insert 75(3(_,4), 8(7,_))
6delete 8one child → bypass: 5(3(_,4), 7)

Applied Exercise

Problem: Delete a node with two children while preserving the BST invariant. Derivation Proof / Hand-Calculation Walkthrough:

Final Extracted Output: substituting the next-largest key preserves left<root<right; the successor has no left child, so its removal is the easy case.

✍️ Practice

🧠 Active Recall