Maximum Width of Binary Tree (LC 662) asks for the widest level of a tree,
counting the null positions a complete tree would have between the leftmost
and rightmost real nodes. The idea that makes it linear is a seat number:
root at 1, children of seat i at 2i and 2i+1, so a level’s width is
just right − left + 1 and the nulls are counted without ever existing.
Everyone gets that far in a minute. The problem’s actual content is what
those seats do on a deep tree — a seat at depth d is a d-bit number,
the constraints promise the answer fits in 32 bits and say nothing about the
seats, and a 3000-node tree that leans all the way left puts a 3000-bit seat
on its last node.
So the piece runs one BFS, level by level, and draws the ghosts — dashed
seats between consecutive real nodes, or a dashed run labelled 4,094 empty when there are too many to draw — with the width bracket under the
current level written out as the subtraction it is. Then it runs the same
walk under four arithmetics, switchable mid-walk on the same level:
- exact — arbitrary precision (Ruby’s
Integer, Python’sint, a JSBigInt). Always right. The bit meter in the side panel climbs one bit per level, forever, and the narration says what that costs the multiply. - double — a JS
Number. Past 2^53 the representable integers thin out:2sand2s+1round to the same value, two children share one seat (both turn red), and the width comes out wrong with no error anywhere. On the left-chain preset at depth 60 the true bottom width is 2 and the double says 1. At depth 1024 it would beInfinity, andInfinity − InfinityisNaN. - int32 — a Java
int. Seats wrap negative at depth 31 (amber) — and the width stays right, because(R − L) mod 2^32isR − Lwhenever the true width is under 2^31, which the constraints guarantee. Correct by modular arithmetic; the viz says so on the level where it happens, because “does this overflow?” is the follow-up, and the answer is yes, and here is why it still works, and here is how to make it not a question. - rebase — subtract the level’s leftmost seat before spawning children, so every level’s seats restart near 0. The meter goes flat: a seat is bounded by twice the previous level’s width, never by the depth.
Presets: the three LC examples, the left chain with a fork at the bottom
(depth adjustable to 80), two spines (two nodes on the bottom level, 2^d − 2
ghosts between them — the width is the ghosts), and random trees with a
lean slider. Space plays and pauses, the arrows step, 1–4 switch the
arithmetic in place, r restarts.
Reuse
src/rebase.js is a framework-free ES module:
fromLevelOrder(arr)— a tree from LeetCode’s[1,3,2,5,3,null,9]notation;chain(depth, side),spines(depth),randomTree(n, lean, rng)— the presets. Trees are flat{ nodes: [{ id, val, left, right }], root }.layout(tree)— in-order x rank and depth per node, plus each level’s nodes left to right. Geometry for a renderer; the algorithm never needs it.simulate(tree, mode)— the walk under'exact' | 'double' | 'int32' | 'rebase'. Returns every level (seats,left,right,width,trueWidth,wrong,bits,trueBits,negative,collided,ghosts) and one frame per level (best,bestLevel,done), withanswerandtrueAnswerfor the run. Same trace-per-tick shape asends-first,staircaseandsift.seatLabel(seat)— a seat as a short string, whatever number type it is.
No rendering or timers in the module; the canvas demo is reference code.
Gotchas
- Seats are 1-based (root at 1) to match the brief’s
2i / 2i+1. With a 0-based root and the same formula the left spine stays at 0 forever and only right-leaning paths grow; the depth story is the same either way, it just leans the other way. Underrebasethe root’s seat is irrelevant. trueWidthand the ghosts are computed from exactBigIntseats in every mode, so the red “really 2” annotation is the truth, not another approximation. Widths past 2^53 (aspinespreset deeper than the 24 the UI allows) would themselves be inexact asNumbers.int32mode models two’s-complement wraparound with| 0. It is a model of Java; in C++ signed overflow is undefined behaviour and the compiler owes you nothing.- The demo bundles its own copy of
rebase.js(self-contained by contract); re-copy after editingsrc/.