2022-04-27 21:37:58 +02:00
|
|
|
function balanceFactor(node) {
|
|
|
|
if (!node) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
let [left, right] = [nodeRank(node.left), nodeRank(node.right)];
|
|
|
|
return right - left;
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateRank(node) {
|
|
|
|
let [left, right] = [nodeRank(node.left), nodeRank(node.right)];
|
|
|
|
node.rank = 1 + Math.max(left, right);
|
|
|
|
}
|
|
|
|
|
|
|
|
class AVLTree extends RankedTree {
|
|
|
|
isCorrectNode(node, recursive) {
|
|
|
|
if (!node) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-05-07 17:11:07 +02:00
|
|
|
let differences = nodeDifferences(node).sort();
|
|
|
|
if (
|
|
|
|
(!differences.equals([1, 1]) && !differences.equals([1, 2])) ||
|
|
|
|
node.rank != 1 + Math.max(...differences)
|
|
|
|
) {
|
2022-04-27 21:37:58 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-05-07 17:11:07 +02:00
|
|
|
recursive = recursive ?? true;
|
2022-05-01 17:09:46 +02:00
|
|
|
return (
|
|
|
|
!recursive ||
|
|
|
|
(this.isCorrectNode(node.left) && this.isCorrectNode(node.right))
|
|
|
|
);
|
2022-04-27 21:37:58 +02:00
|
|
|
}
|
|
|
|
|
2022-05-22 20:27:02 +02:00
|
|
|
fix0Child(x, y, z, rotateLeft, rotateRight) {
|
2022-04-27 21:37:58 +02:00
|
|
|
let newRoot = x.parent;
|
|
|
|
|
|
|
|
if (!y || nodeDifference(y) == 2) {
|
2022-05-22 20:27:02 +02:00
|
|
|
newRoot = rotateRight(this, z);
|
2022-05-16 18:36:31 +02:00
|
|
|
this.record("Fixing 0-child by single rotation: by z", z, newRoot);
|
2022-05-06 10:34:42 +02:00
|
|
|
|
|
|
|
z.demote();
|
2022-05-15 22:28:42 +02:00
|
|
|
this.record("Fixing 0-child by single rotation: demoting z", z);
|
2022-04-27 21:37:58 +02:00
|
|
|
} else if (nodeDifference(y) == 1) {
|
2022-05-22 20:27:02 +02:00
|
|
|
let intermediateRoot = rotateLeft(this, x);
|
2022-05-15 17:53:12 +02:00
|
|
|
this.record(
|
2022-05-15 22:28:42 +02:00
|
|
|
"Fixing 0-child by double-rotation: first rotation by x",
|
2022-05-16 18:36:31 +02:00
|
|
|
x,
|
|
|
|
intermediateRoot
|
2022-05-15 17:53:12 +02:00
|
|
|
);
|
2022-05-01 17:10:24 +02:00
|
|
|
|
2022-05-22 20:27:02 +02:00
|
|
|
newRoot = rotateRight(this, z);
|
2022-05-15 17:53:12 +02:00
|
|
|
this.record(
|
2022-05-15 22:28:42 +02:00
|
|
|
"Fixing 0-child by double-rotation: second rotation by z",
|
2022-05-16 18:36:31 +02:00
|
|
|
z,
|
|
|
|
newRoot
|
2022-05-15 17:53:12 +02:00
|
|
|
);
|
2022-04-27 21:37:58 +02:00
|
|
|
|
|
|
|
y.promote();
|
2022-05-15 22:28:42 +02:00
|
|
|
this.record("Fixing 0-child by double-rotation: promoting y", y);
|
2022-05-06 10:34:42 +02:00
|
|
|
|
2022-04-27 21:37:58 +02:00
|
|
|
x.demote();
|
2022-05-15 22:28:42 +02:00
|
|
|
this.record("Fixing 0-child by double-rotation: demoting x", x);
|
2022-04-27 21:37:58 +02:00
|
|
|
|
2022-05-06 10:34:42 +02:00
|
|
|
z.demote();
|
2022-05-15 22:28:42 +02:00
|
|
|
this.record("Fixing 0-child by double-rotation: demoting z", z);
|
2022-05-01 17:10:24 +02:00
|
|
|
}
|
2022-04-27 21:37:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
insertRebalance(x) {
|
|
|
|
let diffs = nodeDifferences(x.parent).sort();
|
|
|
|
while (x.parent && diffs.equals([0, 1])) {
|
|
|
|
x.parent.promote();
|
2022-05-15 22:28:42 +02:00
|
|
|
this.record(
|
|
|
|
"Insertion rebalance: promoting (0, 1) parent",
|
|
|
|
x.parent
|
|
|
|
);
|
2022-05-01 17:10:24 +02:00
|
|
|
|
2022-05-15 22:28:42 +02:00
|
|
|
x = x.parent;
|
2022-04-27 21:37:58 +02:00
|
|
|
diffs = nodeDifferences(x.parent).sort();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!x.parent) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let rankDifference = nodeDifference(x);
|
|
|
|
if (rankDifference != 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let xParent = x.parent;
|
|
|
|
|
|
|
|
if (rankDifference == 0 && x.parent.left === x) {
|
2022-05-22 20:27:02 +02:00
|
|
|
this.fix0Child(x, x.right, xParent, rotateLeft, rotateRight);
|
2022-04-27 21:37:58 +02:00
|
|
|
} else if (rankDifference == 0 && x.parent.right === x) {
|
2022-05-22 20:27:02 +02:00
|
|
|
this.fix0Child(x, x.left, xParent, rotateRight, rotateLeft);
|
2022-04-27 21:37:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-22 20:27:02 +02:00
|
|
|
deleteRotate(x, y, leaning, rotateLeft, rotateRight) {
|
2022-04-27 21:37:58 +02:00
|
|
|
let newRoot = x;
|
|
|
|
|
|
|
|
let factor = balanceFactor(y);
|
|
|
|
switch (factor) {
|
|
|
|
case 0:
|
|
|
|
case leaning:
|
2022-05-22 20:27:02 +02:00
|
|
|
newRoot = rotateLeft(this, x);
|
2022-04-27 21:37:58 +02:00
|
|
|
break;
|
|
|
|
default:
|
2022-05-22 20:27:02 +02:00
|
|
|
let intermediateRoot = rotateRight(this, y);
|
2022-05-16 18:36:31 +02:00
|
|
|
this.record(
|
|
|
|
"AVL deletion, fixing by double-rotation: by y",
|
|
|
|
y,
|
|
|
|
intermediateRoot
|
|
|
|
);
|
2022-05-07 17:11:07 +02:00
|
|
|
|
2022-05-22 20:27:02 +02:00
|
|
|
newRoot = rotateLeft(this, x);
|
2022-04-27 21:37:58 +02:00
|
|
|
break;
|
|
|
|
}
|
2022-05-16 18:36:31 +02:00
|
|
|
this.record("AVL deletion, fixing by rotation by x", x, newRoot);
|
2022-05-01 17:10:24 +02:00
|
|
|
|
|
|
|
[newRoot.left, newRoot.right, newRoot]
|
|
|
|
.filter((x) => x)
|
|
|
|
.forEach((n) => {
|
|
|
|
updateRank(n);
|
2022-05-15 22:28:42 +02:00
|
|
|
this.record("Updating rank of nodes affected by rotations", n);
|
2022-05-01 17:10:24 +02:00
|
|
|
});
|
|
|
|
|
2022-05-16 19:27:43 +02:00
|
|
|
return factor != 0;
|
2022-04-27 21:37:58 +02:00
|
|
|
}
|
|
|
|
|
2022-05-16 19:27:43 +02:00
|
|
|
deleteFixup(x, parent) {
|
2022-04-27 21:37:58 +02:00
|
|
|
let factor = balanceFactor(x);
|
|
|
|
switch (factor) {
|
|
|
|
case 0:
|
|
|
|
updateRank(x);
|
2022-05-15 17:53:12 +02:00
|
|
|
this.record(
|
2022-05-15 22:28:42 +02:00
|
|
|
"Updating rank of node affected by the propagation of delete",
|
|
|
|
x
|
2022-05-15 17:53:12 +02:00
|
|
|
);
|
2022-05-01 17:10:24 +02:00
|
|
|
|
2022-05-16 19:27:43 +02:00
|
|
|
return true;
|
2022-04-27 21:37:58 +02:00
|
|
|
case -1:
|
|
|
|
case 1:
|
2022-05-16 19:27:43 +02:00
|
|
|
return false;
|
2022-04-27 21:37:58 +02:00
|
|
|
}
|
|
|
|
|
2022-05-01 17:10:24 +02:00
|
|
|
let [z, leaning, toLeft, toRight] = [
|
|
|
|
x.left,
|
|
|
|
-1,
|
|
|
|
rotateRight,
|
|
|
|
rotateLeft,
|
|
|
|
];
|
2022-04-27 21:37:58 +02:00
|
|
|
if (factor == 2) {
|
2022-05-01 17:10:24 +02:00
|
|
|
[z, leaning, toLeft, toRight] = [
|
|
|
|
x.right,
|
|
|
|
1,
|
|
|
|
rotateLeft,
|
|
|
|
rotateRight,
|
|
|
|
];
|
2022-04-27 21:37:58 +02:00
|
|
|
}
|
|
|
|
|
2022-05-22 20:27:02 +02:00
|
|
|
return this.deleteRotate(x, z, leaning, toLeft, toRight);
|
2022-04-27 21:37:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
deleteRebalance(node, parent) {
|
2022-05-16 19:27:43 +02:00
|
|
|
if (!node && !parent) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!node) {
|
|
|
|
[node, parent] = [parent, parent.parent];
|
|
|
|
}
|
|
|
|
|
|
|
|
while (node && this.deleteFixup(node, parent)) {
|
2022-05-01 17:09:46 +02:00
|
|
|
[node, parent] = [parent, parent ? parent.parent : null];
|
2022-04-27 21:37:58 +02:00
|
|
|
}
|
|
|
|
}
|
2022-05-01 17:09:46 +02:00
|
|
|
}
|