class WAVLTree extends AVLTree { isCorrectNode(node, recursive) { if (!node) { return true; } nodeDifferences(node).forEach((childRank) => { if ([1, 2].indexOf(childRank) == -1) { return false; } }); if (node.type() == LEAF) { return node.rank == 0; } recursive = recursive ?? true; return ( !recursive || (this.isCorrectNode(node.left) && this.isCorrectNode(node.right)) ); } fixDelete(x, y, z, reversed, rotateLeft, rotateRight) { let newRoot = x; let [v, w] = [y.left, y.right]; if (reversed) { [v, w] = [w, v]; } let wDiff = nodeDifference(w, y); if (wDiff == 1 && y.parent) { let oldRoot = y.parent; newRoot = rotateLeft(this, y.parent); this.record( "Final step of deletion rebalance: single rotation by parent of y", oldRoot, newRoot ); y.promote(); this.record("Final step of deletion rebalance: promotion of y", y); z.demote(); this.record("Final step of deletion rebalance: demotion of z", z); if (z.type() == LEAF) { z.demote(); this.record( "Final step of deletion rebalance: demotion of leaf z", z ); } } else if (wDiff == 2 && v && v.parent) { let oldRoot = v.parent; let intermediateRoot = rotateRight(this, v.parent); this.record( "Final step of deletion rebalance: first of double rotation by parent of v", oldRoot, intermediateRoot ); oldRoot = v.parent; newRoot = rotateLeft(this, v.parent); this.record( "Final step of deletion rebalance: second of double rotation by parent of v", oldRoot, newRoot ); v.promote().promote(); this.record( "Final step of deletion rebalance: double promotion of v", v ); y.demote(); this.record("Final step of deletion rebalance: demotion of y", y); z.demote().demote(); this.record( "Final step of deletion rebalance: double demotion of z", z ); } } bottomUpDelete(x, parent) { let xDiff = nodeDifference(x, parent); if (xDiff != 3 || !parent) { return; } let y = parent.left === x ? parent.right : parent.left; let yDiff = nodeDifference(y, parent); while ( parent && xDiff == 3 && y && (yDiff == 2 || nodeDifferences(y).equals([2, 2])) ) { parent.demote(); this.record("Propagating error by demoting parent", parent); if (yDiff != 2) { y.demote(); this.record("Demoting y", y); } x = parent; parent = x.parent; if (!parent) { return; } y = parent.left === x ? parent.right : parent.left; xDiff = nodeDifference(x, parent); yDiff = nodeDifference(y, parent); } if (!parent) { return; } let parentNodeDiffs = nodeDifferences(parent); if (parentNodeDiffs.sort().equals([1, 3])) { if (parent.left === x) { this.fixDelete( x, parent.right, parent, false, rotateLeft, rotateRight ); } else { this.fixDelete( x, parent.left, parent, true, rotateRight, rotateLeft ); } } } deleteFixup(y, parent) { if (nodeDifferences(y).equals([2, 2])) { y.demote(); this.record( "Starting deletion rebalance by demoting (2, 2) node", y ); parent = y.parent; } else if (nodeDifferences(parent).equals([2, 2])) { parent.demote(); this.record( "Starting deletion rebalance by demoting (2, 2) node", parent ); parent = parent.parent; } if (!parent) { return; } [parent.left, parent.right].forEach((y) => { if (nodeDifference(y, parent) == 3) { this.bottomUpDelete(y, parent); } }); } deleteRebalance(node, parent) { this.deleteFixup(node, parent); } }