A merge can leave the parent internal node too empty to stand, and internal nodes merge with a twist - the separator is pulled down into the merged node. Today you handle an underflowing internal node so a merge cascades correctly up the tree.
When a merge underflows an internal node, merge it with a sibling by pulling the parent's separator down into the combined node.
An internal node underflows when a merge below it removes one of its keys and it drops under the minimum. It repairs the same two ways a leaf does - borrow or merge - but merging is where the shapes diverge. A leaf merge drops the separator; an internal merge pulls it down. The separator between the two nodes is a real boundary key that is not stored in any leaf, so it must become the middle key of the merged node, sitting between the two nodes’ child pointers, or the key space would develop a gap.
So the merged internal node is left keys, then the pulled-down separator, then right
keys, with all the children concatenated in order - the exact inverse of the
splitInternal promotion. Removing that separator from the parent can underflow the
parent in turn, so the repair recurses upward, borrowing or merging at each
level until it reaches a node that is still legal. The one place it can run out of
tree entirely is the root, which is where the last delete lesson comes in.
// internal merge (right sibling into left): the merged node's keys are// L.Keys ++ [parentSeparator] ++ R.Keys (separator pulled DOWN)// and its children are L.Children ++ R.Children.// then remove that separator and R's pointer from the parent, which// may itself underflow -> recurse the same repair upward.