feat(rbt): implement red-black tree
Signed-off-by: Matej Focko <mfocko@redhat.com>
This commit is contained in:
parent
df787476ac
commit
dce3e3c11c
3 changed files with 199 additions and 0 deletions
|
@ -148,6 +148,18 @@
|
|||
<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>
|
||||
|
@ -190,6 +202,18 @@
|
|||
<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>
|
||||
|
@ -211,6 +235,7 @@
|
|||
<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,6 +115,18 @@
|
|||
<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>
|
||||
|
@ -186,6 +198,7 @@
|
|||
<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>
|
||||
|
||||
|
|
161
rbt.js
Normal file
161
rbt.js
Normal file
|
@ -0,0 +1,161 @@
|
|||
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(p, this);
|
||||
this.record("Insertion case #2: rotating by parent");
|
||||
} else {
|
||||
// Case 3
|
||||
// ======
|
||||
// z’s uncle y is black and z is a left child
|
||||
rotateRight(pp, this);
|
||||
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(parent, this);
|
||||
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(w, this);
|
||||
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(parent, this);
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue