web/wavl.js
Matej Focko 5388f21f04
fix(rotations): use built-in root adjustment
• remove unnecessary parameters to helper functions
• use built-in root adjustment of the rotate functions
• swap parameters of the rotation functions, to pass the tree first

Signed-off-by: Matej Focko <mfocko@redhat.com>
2022-05-22 20:27:02 +02:00

185 lines
4.9 KiB
JavaScript

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);
}
}