162 lines
No EOL
4 KiB
JavaScript
162 lines
No EOL
4 KiB
JavaScript
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];
|
|
}
|
|
}
|
|
} |