Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

After this page, you should be able to:

Builds on: AVL insertion, AVL deletion and Topic 7’s OrderedSet adapter.

Code languages: Python defines the explanations, figures and self-checks. Use the language guide for native conventions and dependencies. Complete runnable topic examples: Python · JavaScript · Julia.

The same BST readers and OrderedSet interface are reused from Topic 7. Julia updates use function calls, not dot methods. All variants use empty height −1, leaf height 0, and left height minus right height; rotations must return and reconnect the new subtree root.

Purpose and operation contracts

AVL balancing changes how short the paths stay, not what a set operation means. Use the same operation table: add ignores duplicates; remove is strict and returns None; discard tolerates absence; inspections leave the tree unchanged; extraction removes and returns an extreme. Missing extremes or neighbours raise KeyError in that adapter.

The raw avl_insert and avl_delete helpers instead return the resulting subtree root. Missing-key raw deletion leaves the key set unchanged. Do not mistake this low-level policy for the public strict-remove policy, or a rotation’s root result for an inserted/deleted record.

Representation and all maintained invariants

The first invariant makes ordinary BST queries correct; the next two bound path length; the last makes size/empty constant time. AVLNode stores no parent pointer, cached minimum or subtree counts for rank/select.

Trace the same operations through a different shape

Insert 30,10,20. The insertion reading’s LR repair yields root 20 with children 10 and 30. Repeating add(20) changes neither the keys nor the count. Successor(15) returns 20 without mutation. Extract-minimum returns 10; the remaining sorted keys are 20,30, regardless of any valid shape chosen by later repairs.

The LR insertion trace checks heights bottom-up and repairs the tree to root 20, with 10 and 30 retained.

Python implementation and reuse

Read the Topic 7 query helpers and adapter, then Topic 8’s node/height, rotation, rebalance, insertion and deletion definitions in one session. The shared readers inspect only key, left and right; no separate AVL search algorithm is required.

Python
JavaScript
Julia
balanced = OrderedSet(avl_insert, avl_delete)
for key in [30, 10, 20]:
    balanced.add(key)
balanced.add(20)
assert balanced.size() == 3
assert balanced.keys() == [10, 20, 30]
assert balanced.successor(15) == 20
assert balanced.range_keys(15, 30) == [20, 30]
assert balanced.extract_min() == 10
assert balanced.keys() == [20, 30]

Implementation choices and correctness

The adapter always reconnects the updated root, including after rotations. Only the AVL insert/delete pair may mutate this AVL-backed instance; calling ordinary bst_insert on its nodes could introduce nodes without height metadata and bypass balancing. Read-only BST queries are safe to reuse because AVL ordering is the same.

Using cached heights makes each local check constant work. Recomputing whole-subtree heights at every ancestor would change the update cost. A manual rotation preserves key order when its structural precondition holds, but it need not restore balance unless chosen by the repair algorithm.

Time and space costs

Let L=log₂(n+2), so even an empty-tree operation has a constant bound.

OperationWorst-case timeExtra workspace, excluding results
Exact lookup / membershipO(L)O(1), iterative
Add/remove/discardO(L), including adapter prechecksO(L), recursive update stack
Minimum/maximum; successor/predecessorO(L)O(1), iterative
Extract minimum/maximumO(L), several paths by a constant factorO(L) during deletion
Preorder/inorder/postorder snapshotO(n+1)O(L) stack, plus O(n) result
Closed range, r answersO(L+r)O(L) stack, plus O(r) result
Size / emptyO(1)O(1)
Build by n repeated insertionsO(n log(n+2))O(L) stack beyond the growing tree

Persistent storage is O(n+1), including one height per node and the adapter’s total count. Rotations use O(1) extra local storage. One insertion needs at most one single/double repair at the lowest unbalanced ancestor, though this code still returns through all ancestors. Deletion may repair several levels and still stays logarithmic. Traversal and reporting all keys cannot be logarithmic because the output itself has n items.

When to use it

AVL trees suit changing ordered data when worst-case path bounds matter. They require more update bookkeeping than an ordinary BST. For one static collection, compare sorted-array search and its preparation costs; for exact-key membership alone, compare a hash set’s qualified expected bounds. The common contract allows behaviour to be compared separately from these trade-offs.

Check your understanding

1. Can the iterative BST successor helper be used on AVL nodes without computing balance factors?

Show explanation

Yes. It only reads the BST ordering and links. AVL height bounds make the same path logarithmic; balance factors are needed for updates, not this read-only query.

2. Is a sorted inorder list enough to validate an AVL update?

Show explanation

No. Also check strict ancestor bounds, actual and cached heights, every node’s balance, and the adapter count. An ordered tree may still be unbalanced or hold stale metadata.

Continue in sidebar order to spatial search, which changes the partition invariant. For the graph route, you can instead go directly to priority queues and heaps, or return to balanced search trees.