TAOCP 6.2.3 Exercise 21
The previous solution fails because it introduces unnecessary hierarchical structure that does not preserve the global constraint from Exercise 5.
Exercise 21. [30] [30] (Perfect balancing.) Design an algorithm to construct N-node binary trees that are optimum in the sense of exercise 5. Your algorithm should use O(V) steps and it should be “online,” in the sense that it inputs the nodes one by one in increasing order and builds partial trees as it goes, without knowing the final value of N in advance. (It would be appropriate to use such an algorithm when restructuring a badly balanced tree, or when merging the keys of two trees into a single tree.)
Verified: no
Solve time: 5m46s
Corrected Solution
The previous solution fails because it introduces unnecessary hierarchical structure that does not preserve the global constraint from Exercise 5. The correct approach is much simpler: the optimal trees in Exercise 5 have a pure shape characterization, and that shape can be maintained directly in an online way without decomposition.
1. What “optimal” means (Exercise 5)
Exercise 5 characterizes optimal binary trees as those in which all external nodes lie on two adjacent levels.
Equivalently, this is the same as saying:
- The tree is a complete binary tree, except possibly for the last level,
- and the last level is filled from left to right with no gaps.
Thus, for each $N$, there is a unique shape: the standard level-order complete binary tree with $N$ nodes.
So the problem reduces to:
Maintain the shape of a complete binary tree under insertions, online, in $O(N)$ total time.
No additional structural decomposition is needed.
2. Key idea
We maintain the tree exactly as a level-order complete binary tree being filled incrementally.
This is naturally supported by tracking nodes in the order they become eligible to receive children.
We maintain a queue of nodes that are not yet full (i.e., they have fewer than two children).
3. Data structure
Maintain:
- A pointer
root - A queue $Q$ of nodes that have fewer than 2 children
Each node stores:
left,rightpointers (initially null)
4. Insertion algorithm
We input nodes $x_1, x_2, \dots, x_N$ in order.
Each new node is created and attached as follows:
Algorithm INSERT(x)
- Create a new node $v$ with key $x$, and set $v.left = v.right = \text{null}$.
- If the tree is empty:
- set
root = v - enqueue $v$ into $Q$
- return
- Let $u = Q.\text{front}()$.
- If $u.left = \text{null}$, attach:
- $u.left \leftarrow v$
- else:
- $u.right \leftarrow v$
- dequeue $u$ from $Q$ (it is now full)
- enqueue $v$ into $Q$
5. Why this maintains the correct shape
We prove that after each insertion, the tree is the unique optimal shape for its size.
Lemma 1 (level-order completeness invariant)
At every step, nodes are filled in breadth-first order, left to right.
Reason:
- The queue always contains exactly the nodes that still have an open child position.
- The front of the queue is always the leftmost node at the shallowest level with available capacity.
- New nodes are always attached to the earliest possible position.
Thus, no position in the tree is ever skipped.
Lemma 2 (completeness of levels)
When a level is not completely filled, only that level receives new nodes, from left to right.
Once a level becomes full, insertion proceeds to the next level.
Therefore:
- All levels except possibly the last are completely full.
- The last level is filled left-to-right.
This is exactly the definition of a complete binary tree.
Lemma 3 (optimality condition)
In a complete binary tree:
- all external nodes (null children) appear only on the last level or the level below it,
- hence they lie on two adjacent levels.
Thus the tree is optimal in the sense of Exercise 5.
6. Correctness of online property
The algorithm never assumes knowledge of $N$:
- Each insertion uses only the current frontier stored in $Q$.
- No rebalancing or restructuring occurs.
- The tree after $k$ insertions depends only on $k$, not future inputs.
Hence it is fully online.
7. Complexity analysis
Each node:
- is enqueued once,
- is dequeued once,
- and has at most two pointer assignments.
Therefore:
- Each insertion performs $O(1)$ amortized work,
- Total work over $N$ insertions is $O(N)$,
- Space usage is $O(N)$.
8. Why the previous approach was unnecessary
The flawed solution attempted to:
- decompose the tree into blocks,
- build a meta-tree,
- and perform substitutions.
This is incorrect because optimality is a global shape property, not preserved under arbitrary hierarchical composition.
The correct construction shows that:
the optimal shape is inherently sequential and can be maintained directly via breadth-first insertion.
Final result
The algorithm maintains, after each insertion, the unique optimal binary tree with $k$ nodes by enforcing level-order completeness using a queue of incomplete nodes.
It is online and runs in $O(N)$ total time.