web/avl.js
Matej Focko 70c3b55f1a
chore: format sources
Signed-off-by: Matej Focko <mfocko@redhat.com>
2022-05-01 17:09:54 +02:00

166 lines
4.1 KiB
JavaScript

// TODO: Factor out to common module
Array.prototype.equals = function (other) {
if (this.length != other.length) {
return false;
}
let i = this.length;
while (--i >= 0) {
if (this[i] !== other[i]) {
return false;
}
}
return true;
};
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;
}
recursive = recursive ?? true;
// FIXME: not enough, must be checked against differences
// let bf = balanceFactor(node);
// if (bf < -1 || bf > 1) {
// return false;
// }
let differences = nodeDifferences(node);
differences.sort();
if (!differences.equals([1, 1]) && !differences.equals([1, 2])) {
return false;
}
return (
!recursive ||
(this.isCorrectNode(node.left) && this.isCorrectNode(node.right))
);
}
fix0Child(x, y, z, rotateLeft, rotateRight) {
let newRoot = x.parent;
if (!y || nodeDifference(y) == 2) {
newRoot = rotateRight(z);
z.demote();
} else if (nodeDifference(y) == 1) {
rotateLeft(x);
newRoot = rotateRight(z);
y.promote();
x.demote();
z.demote();
}
return newRoot;
}
insertRebalance(x) {
let diffs = nodeDifferences(x.parent).sort();
while (x.parent && diffs.equals([0, 1])) {
x.parent.promote();
x = x.parent;
diffs = nodeDifferences(x.parent).sort();
}
if (!x.parent) {
return;
}
let rotatingAroundRoot = x.parent.parent == null;
let newRoot = x.parent;
let rankDifference = nodeDifference(x);
if (rankDifference != 0) {
return;
}
let xParent = x.parent;
if (rankDifference == 0 && x.parent.left === x) {
newRoot = this.fix0Child(
x, x.right, xParent, rotateLeft, rotateRight
);
} else if (rankDifference == 0 && x.parent.right === x) {
newRoot = this.fix0Child(
x, x.left, xParent, rotateRight, rotateLeft
);
}
if (rotatingAroundRoot) {
this.root = newRoot;
}
}
deleteRotate(x, y, leaning, rotatingAroundRoot, rotateLeft, rotateRight) {
let newRoot = x;
let factor = balanceFactor(y);
switch (factor) {
case 0:
case leaning:
newRoot = rotateLeft(x);
break;
default:
rotateRight(y);
newRoot = rotateLeft(x);
break;
}
[newRoot.left, newRoot.right, newRoot].filter(x => x).forEach(n => {
updateRank(n)
});
if (rotatingAroundRoot) {
this.root = newRoot;
}
return factor == 0;
}
deleteFixup(y, parent) {
let x = y ? y : parent;
let factor = balanceFactor(x);
switch (factor) {
case 0:
updateRank(x);
return false;
case -1:
case 1:
return true;
}
let rotatingAroundRoot = x.parent == null;
let [z, leaning, toLeft, toRight] = [x.left, -1, rotateRight, rotateLeft];
if (factor == 2) {
[z, leaning, toLeft, toRight] = [x.right, 1, rotateLeft, rotateRight];
}
return this.deleteRotate(x, z, leaning, rotatingAroundRoot, toLeft, toRight);
}
deleteRebalance(node, parent) {
while (node || parent) {
this.deleteFixup(node, parent);
[node, parent] = [parent, parent ? parent.parent : null];
}
}
}