web/ranked_tree.js
Matej Focko eb0b2a39f6
feat: implement recorder and visualization
Signed-off-by: Matej Focko <mfocko@redhat.com>
2022-05-01 17:10:24 +02:00

191 lines
4.1 KiB
JavaScript

class Queue {
constructor() {
this.q = [];
}
enqueue(x) {
this.q.push(x);
}
dequeue() {
return this.q.shift();
}
isEmpty() {
return this.q.length == 0;
}
}
class RankedTree {
constructor() {
this.root = null;
this.recorder = null;
}
_getUnwrappedGraph() {
let result = [];
let q = new Queue();
q.enqueue(this.root);
let edges = [];
while (!q.isEmpty()) {
let node = q.dequeue();
if (!q) {
continue;
}
result.push(
`\t"${node.toString()}" [label="${node.value}, ${node.rank}"];`
);
[node.left, node.right]
.filter((child) => child)
.forEach((child) => {
edges.push([node, child]);
q.enqueue(child);
});
}
result.push("");
edges.forEach((vertices) => {
let [u, v] = vertices;
result.push(
`\t"${u.toString()}" -> "${v.toString()}" [label="${nodeDifference(
v
)}"]`
);
});
return result;
}
toDot() {
return ["digraph {", ...this._getUnwrappedGraph(), "}"];
}
toString() {
return this.toDot().join("\n");
}
record() {
if (!this.recorder) {
return;
}
this.recorder.record(this);
}
rank() {
return nodeRank(this.root);
}
isCorrect() {
return this.isCorrectNode(this.root);
}
search(value, node) {
return nodeSearch(value, node || this.root);
}
insert(value) {
let insertedNode = new Node(value);
if (!this.root) {
this.root = insertedNode;
this.record();
return;
}
let parent = findParentNode(value, this.root);
if (!parent) {
return;
}
insertedNode.parent = parent;
if (value < parent.value) {
parent.left = insertedNode;
} else {
parent.right = insertedNode;
}
this.record();
this.insertRebalance(insertedNode);
}
transplant(u, v) {
if (!u.parent) {
this.root = v;
} else if (u.parent.left === u) {
u.parent.left = v;
} else {
u.parent.right = v;
}
if (v) {
v.rank = u.rank;
v.parent = u.parent;
}
}
deleteNode(node) {
if (!node) {
return null;
}
let [y, parent] = [null, node.parent];
if (!node.left) {
y = node.right;
this.transplant(node, node.right);
} else if (!node.right) {
y = node.left;
this.transplant(node, node.left);
} else {
let successor = nodeMinimum(node.right);
parent = successor.parent != node ? successor.parent : successor;
if (successor.parent != node) {
parent = successor.right ? successor.right : successor.parent;
this.transplant(successor, successor.right);
successor.right = node.right;
successor.right.parent = successor;
}
this.transplant(node, successor);
successor.left = node.left;
node.left.parent = successor;
}
this.record();
return [y, parent];
}
delete(value) {
let node = this.root;
while (node && node.value != value) {
node = value < node.value ? node.left : node.right;
}
let toRebalance = this.deleteNode(node);
if (toRebalance) {
let [y, parent] = toRebalance;
this.deleteRebalance(y, parent);
}
}
/* abstract methods */
isCorrectNode(node) {
throw "not implemented!";
}
insertRebalance(node) {
throw "not implemented!";
}
deleteRebalance(node, parent) {
throw "not implemented!";
}
}