Theory

There is no deletion here, and what that costs

avl.go has no Delete. That is a decision, not a hole, and it is worth saying what it costs.

What writing it would involve

Deleting from an AVL tree is lesson 11's second drill (leaf / one child / two children via the successor), followed by rebalance on the way back up. Implemented and measured:

after 10,000 deletions from 20,000: len 10000, height 14 (bound 18.8), 4997 rotations

It works. The bound holds. The ordering survives.

How much code:

lines (excluding comments and blanks)
Put 21
Delete 40

The tree's mutation code goes from 21 lines to 61 — roughly triple.

And what you would learn from it

Nothing.

The rotations are the same four. rebalance is the same function, untouched. The invariant is the same. The bound is the same.

Only two things are new, and both are bookkeeping rather than an idea:

  • when deleting a node with two children, the path to repair is the successor's, not the deleted node's;
  • an insertion needs at most one repair point, after which the heights stop changing; a deletion can cascade repairs all the way to the root.

The second is the only real difference and it fits in one sentence. Paying forty lines and half a lesson for it is a bad trade.

This lesson's idea is an invariant that bounds the height, and you have it already. Deletion applies the same idea in one more place.

So deletion is a coursework option rather than a step. Anyone who wants it has everything needed: the rotations, rebalance, the bound, and a test that will say whether it worked.

Where trees like this actually live

Briefly, because it was checked rather than guessed.

transit has no balanced tree — lesson 11 enumerated every type in the repository and there is no tree of any kind. Nor does it need one: its indexes are built once and never modified, and in that case a sorted slice with a binary search is perfectly balanced for free.

Nor does Go's standard library. The entire container package is:

container/heap    container/list    container/ring

A heap, a doubly linked list and a ring. No tree. Go's answer for an ordered set is different — sort a slice and use sort.Search, the same lowerBound you wrote in lesson 6.

That does not mean balanced trees are unnecessary. It means they live where the data keeps changing and has to stay ordered — database indexes, file systems, kernel schedulers. Your library index is exactly that kind of place: Put can be called at any time.