Compare commits
No commits in common. "main" and "submit" have entirely different histories.
8 changed files with 62 additions and 260 deletions
|
@ -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
55
avl.js
|
@ -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) {
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
13
index.html
13
index.html
|
@ -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>
|
||||
|
||||
|
|
8
node.js
8
node.js
|
@ -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;
|
||||
|
|
|
@ -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
173
rbt.js
|
@ -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
|
||||
// ======
|
||||
// z’s uncle y is red
|
||||
pp.rank++;
|
||||
this.record("Insertion case #1: recoloring uncle", pp);
|
||||
z = pp;
|
||||
} else if (z === rightChild) {
|
||||
// Case 2
|
||||
// ======
|
||||
// z’s 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
|
||||
// ======
|
||||
// z’s 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
|
||||
// ======
|
||||
// x’s 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
|
||||
// ======
|
||||
// x’s sibling w is black, and both of w’s children are black
|
||||
parent.rank--;
|
||||
this.record("Deletion case #2: recoloring sibling", w);
|
||||
x = parent;
|
||||
} else {
|
||||
// Case 3
|
||||
// ======
|
||||
// x’s sibling w is black,
|
||||
// w’s left child is red, and w’s 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
|
||||
// ======
|
||||
// x’s sibling w is black, and w’s 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
19
wavl.js
|
@ -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
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue