Compare commits

..

No commits in common. "main" and "submit" have entirely different histories.
main ... submit

8 changed files with 62 additions and 260 deletions

View file

@ -1,21 +0,0 @@
pages:
script:
- mkdir .public
- cp -r * .public
- mv .public public
artifacts:
paths:
- public
only:
- main
pre-commit:
variables:
PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit
cache:
paths:
- ${PRE_COMMIT_HOME}
image: registry.gitlab.com/yesolutions/docker-pre-commit
script:
- pip install pre-commit
- pre-commit run --all-files

55
avl.js
View file

@ -33,24 +33,30 @@ class AVLTree extends RankedTree {
);
}
fix0Child(x, y, z, rotateLeft, rotateRight) {
fix0Child(x, y, z, rotatingAroundRoot, rotateLeft, rotateRight) {
let newRoot = x.parent;
if (!y || nodeDifference(y) == 2) {
newRoot = rotateRight(this, z);
newRoot = rotateRight(z);
if (rotatingAroundRoot) {
this.root = newRoot;
}
this.record("Fixing 0-child by single rotation: by z", z, newRoot);
z.demote();
this.record("Fixing 0-child by single rotation: demoting z", z);
} else if (nodeDifference(y) == 1) {
let intermediateRoot = rotateLeft(this, x);
let intermediateRoot = rotateLeft(x);
this.record(
"Fixing 0-child by double-rotation: first rotation by x",
x,
intermediateRoot
);
newRoot = rotateRight(this, z);
newRoot = rotateRight(z);
if (rotatingAroundRoot) {
this.root = newRoot;
}
this.record(
"Fixing 0-child by double-rotation: second rotation by z",
z,
@ -85,6 +91,8 @@ class AVLTree extends RankedTree {
return;
}
let rotatingAroundRoot = x.parent.parent == null;
let rankDifference = nodeDifference(x);
if (rankDifference != 0) {
return;
@ -93,32 +101,49 @@ class AVLTree extends RankedTree {
let xParent = x.parent;
if (rankDifference == 0 && x.parent.left === x) {
this.fix0Child(x, x.right, xParent, rotateLeft, rotateRight);
this.fix0Child(
x,
x.right,
xParent,
rotatingAroundRoot,
rotateLeft,
rotateRight
);
} else if (rankDifference == 0 && x.parent.right === x) {
this.fix0Child(x, x.left, xParent, rotateRight, rotateLeft);
this.fix0Child(
x,
x.left,
xParent,
rotatingAroundRoot,
rotateRight,
rotateLeft
);
}
}
deleteRotate(x, y, leaning, rotateLeft, rotateRight) {
deleteRotate(x, y, leaning, rotatingAroundRoot, rotateLeft, rotateRight) {
let newRoot = x;
let factor = balanceFactor(y);
switch (factor) {
case 0:
case leaning:
newRoot = rotateLeft(this, x);
newRoot = rotateLeft(x);
break;
default:
let intermediateRoot = rotateRight(this, y);
let intermediateRoot = rotateRight(y);
this.record(
"AVL deletion, fixing by double-rotation: by y",
y,
intermediateRoot
);
newRoot = rotateLeft(this, x);
newRoot = rotateLeft(x);
break;
}
if (rotatingAroundRoot) {
this.root = newRoot;
}
this.record("AVL deletion, fixing by rotation by x", x, newRoot);
[newRoot.left, newRoot.right, newRoot]
@ -147,6 +172,7 @@ class AVLTree extends RankedTree {
return false;
}
let rotatingAroundRoot = x.parent == null;
let [z, leaning, toLeft, toRight] = [
x.left,
-1,
@ -162,7 +188,14 @@ class AVLTree extends RankedTree {
];
}
return this.deleteRotate(x, z, leaning, toLeft, toRight);
return this.deleteRotate(
x,
z,
leaning,
rotatingAroundRoot,
toLeft,
toRight
);
}
deleteRebalance(node, parent) {

View file

@ -148,18 +148,6 @@
<label class="btn btn-outline-primary" for="lRavlTreeBtn"
>rAVL</label
>
<input
type="radio"
class="btn-check"
name="leftTreeSelection"
id="lRbTreeBtn"
autocomplete="off"
onclick="switchTree(RBTree, 'left')"
/>
<label class="btn btn-outline-primary" for="lRbTreeBtn"
>Red-Black</label
>
</div>
</div>
</div>
@ -202,18 +190,6 @@
<label class="btn btn-outline-primary" for="rRavlTreeBtn"
>rAVL</label
>
<input
type="radio"
class="btn-check"
name="rightTreeSelection"
id="rRbTreeBtn"
autocomplete="off"
onclick="switchTree(RBTree, 'right')"
/>
<label class="btn btn-outline-primary" for="rRbTreeBtn"
>Red-Black</label
>
</div>
</div>
</div>
@ -235,7 +211,6 @@
<script src="avl.js"></script>
<script src="wavl.js"></script>
<script src="ravl.js"></script>
<script src="rbt.js"></script>
<script src="recorder.js"></script>

View file

@ -115,18 +115,6 @@
<label class="btn btn-outline-primary" for="ravlTreeBtn"
>rAVL</label
>
<input
type="radio"
class="btn-check"
name="treeSelection"
id="rbTreeBtn"
autocomplete="off"
onclick="switchTree(RBTree)"
/>
<label class="btn btn-outline-primary" for="rbTreeBtn"
>Red-Black</label
>
</div>
</div>
</div>
@ -198,7 +186,6 @@
<script src="avl.js"></script>
<script src="wavl.js"></script>
<script src="ravl.js"></script>
<script src="rbt.js"></script>
<script src="recorder.js"></script>

View file

@ -36,7 +36,7 @@ function nodeDifferences(node) {
return [r - nodeRank(left), r - nodeRank(right)];
}
function rotateRight(t, x) {
function rotateRight(x) {
let parent = x.parent;
let y = x.left;
// let z = x.right;
@ -47,8 +47,6 @@ function rotateRight(t, x) {
} else {
parent.right = y;
}
} else if (t) {
t.root = y;
}
x.left = y.right;
@ -63,7 +61,7 @@ function rotateRight(t, x) {
return y;
}
function rotateLeft(t, x) {
function rotateLeft(x) {
let parent = x.parent;
// let y = x.left;
let z = x.right;
@ -74,8 +72,6 @@ function rotateLeft(t, x) {
} else {
parent.right = z;
}
} else if (t) {
t.root = z;
}
x.right = z.left;

View file

@ -37,12 +37,10 @@ class RankedTree {
edges.forEach((vertices) => {
let [u, v] = vertices;
let diff = nodeDifference(v);
result.push(
`\t"Node(${u.value})" -> "Node(${
v.value
})" [label="${diff}"${this.styleEdge(diff)}];\n`
})" [label="${nodeDifference(v)}"];\n`
);
});
@ -178,8 +176,4 @@ class RankedTree {
deleteRebalance(node, parent) {
throw "not implemented!";
}
styleEdge(rankDifference) {
return "";
}
}

173
rbt.js
View file

@ -1,173 +0,0 @@
const RED = 0;
const BLACK = 1;
function isDoubleBlack(x) {
return x && nodeDifferences(x).indexOf(2) != -1;
}
class RBTree extends RankedTree {
isCorrectNode(node, recursive) {
if (!node) {
return true;
}
let [left, right] = nodeDifferences(node);
if ([RED, BLACK].indexOf(left) == -1) {
// left subtree has invalid difference
return false;
} else if ([RED, BLACK].indexOf(right) == -1) {
// right subtree has invalid difference
return false;
}
if (nodeDifference(node) == RED && (left == RED || right == RED)) {
// two consecutive red nodes
return false;
}
recursive = recursive ?? true;
return (
!recursive ||
(this.isCorrectNode(node.left) && this.isCorrectNode(node.right))
);
}
insertRebalanceStep(z, y, rightChild, rotateLeft, rotateRight) {
let [p, pp] = [z.parent, z.parent.parent];
if (y && nodeDifference(y) == RED) {
// Case 1
// ======
// zs uncle y is red
pp.rank++;
this.record("Insertion case #1: recoloring uncle", pp);
z = pp;
} else if (z === rightChild) {
// Case 2
// ======
// zs uncle y is black and z is a right child
z = p;
rotateLeft(this, p);
this.record("Insertion case #2: rotating by parent");
} else {
// Case 3
// ======
// zs uncle y is black and z is a left child
rotateRight(this, pp);
this.record("Insertion case #3: rotating by grandparent");
}
return z;
}
insertRebalance(z) {
while (z.parent && nodeDifference(z.parent) == RED) {
let [p, pp] = [z.parent, z.parent.parent];
if (p === pp.left) {
z = this.insertRebalanceStep(
z,
pp.right,
p.right,
rotateLeft,
rotateRight
);
} else {
z = this.insertRebalanceStep(
z,
pp.left,
p.left,
rotateRight,
rotateLeft
);
}
}
}
deleteRebalanceStep(x, w, parent, right, rotateLeft, rotateRight) {
if (nodeDifference(w) == RED) {
// Case 1
// ======
// xs sibling w is red
rotateLeft(this, parent);
this.record("Deletion case #1: rotating by parent", parent);
w = right(parent);
}
if (nodeDifferences(w).equals([BLACK, BLACK])) {
// Case 2
// ======
// xs sibling w is black, and both of ws children are black
parent.rank--;
this.record("Deletion case #2: recoloring sibling", w);
x = parent;
} else {
// Case 3
// ======
// xs sibling w is black,
// ws left child is red, and ws right child is black
if (nodeDifference(right(w), w) == BLACK) {
rotateRight(this, w);
this.record("Deletion case #3: rotating by w", w);
w = right(parent);
}
// Case 4
// ======
// xs sibling w is black, and ws right child is red
parent.rank--;
this.record(
"Deletion case #4: moving double black to parent",
parent
);
w.rank++;
this.record("Deletion case #4: coloring w's child", w);
rotateLeft(this, parent);
this.record("Deletion case #4: rotating by parent", parent);
x = this.root;
}
return [x, x ? x.parent : null];
}
deleteRebalance(node, parent) {
if (!node && !parent) {
return;
}
while (node !== this.root && isDoubleBlack(parent)) {
if (node === parent.left) {
[node, parent] = this.deleteRebalanceStep(
node,
parent.right,
parent,
(x) => x.right,
rotateLeft,
rotateRight
);
} else {
[node, parent] = this.deleteRebalanceStep(
node,
parent.left,
parent,
(x) => x.left,
rotateRight,
rotateLeft
);
}
}
}
styleEdge(rankDifference) {
switch (rankDifference) {
case 2:
return " penwidth=2";
case 0:
return ', color="red"';
case 1:
default:
return "";
}
}
}

19
wavl.js
View file

@ -21,7 +21,7 @@ class WAVLTree extends AVLTree {
);
}
fixDelete(x, y, z, reversed, rotateLeft, rotateRight) {
fixDelete(x, y, z, reversed, rotatingAroundRoot, rotateLeft, rotateRight) {
let newRoot = x;
let [v, w] = [y.left, y.right];
@ -30,9 +30,13 @@ class WAVLTree extends AVLTree {
}
let wDiff = nodeDifference(w, y);
if (wDiff == 1 && y.parent) {
let oldRoot = y.parent;
newRoot = rotateLeft(this, y.parent);
newRoot = rotateLeft(y.parent);
if (rotatingAroundRoot) {
this.root = newRoot;
}
this.record(
"Final step of deletion rebalance: single rotation by parent of y",
oldRoot,
@ -54,7 +58,7 @@ class WAVLTree extends AVLTree {
}
} else if (wDiff == 2 && v && v.parent) {
let oldRoot = v.parent;
let intermediateRoot = rotateRight(this, v.parent);
let intermediateRoot = rotateRight(v.parent);
this.record(
"Final step of deletion rebalance: first of double rotation by parent of v",
oldRoot,
@ -62,7 +66,10 @@ class WAVLTree extends AVLTree {
);
oldRoot = v.parent;
newRoot = rotateLeft(this, v.parent);
newRoot = rotateLeft(v.parent);
if (rotatingAroundRoot) {
this.root = newRoot;
}
this.record(
"Final step of deletion rebalance: second of double rotation by parent of v",
oldRoot,
@ -125,6 +132,8 @@ class WAVLTree extends AVLTree {
return;
}
let rotatingAroundRoot = parent.parent == null;
let parentNodeDiffs = nodeDifferences(parent);
if (parentNodeDiffs.sort().equals([1, 3])) {
if (parent.left === x) {
@ -133,6 +142,7 @@ class WAVLTree extends AVLTree {
parent.right,
parent,
false,
rotatingAroundRoot,
rotateLeft,
rotateRight
);
@ -142,6 +152,7 @@ class WAVLTree extends AVLTree {
parent.left,
parent,
true,
rotatingAroundRoot,
rotateRight,
rotateLeft
);