2022-04-27 21:37:58 +02:00
|
|
|
class RankedTree {
|
|
|
|
constructor() {
|
|
|
|
this.root = null;
|
2022-05-01 17:10:24 +02:00
|
|
|
this.recorder = null;
|
2022-04-27 21:37:58 +02:00
|
|
|
}
|
|
|
|
|
2022-05-15 22:28:42 +02:00
|
|
|
_getUnwrappedGraph(highlightedNode) {
|
2022-05-01 17:10:24 +02:00
|
|
|
let result = [];
|
2022-04-27 21:37:58 +02:00
|
|
|
|
|
|
|
let q = new Queue();
|
|
|
|
q.enqueue(this.root);
|
|
|
|
|
|
|
|
let edges = [];
|
|
|
|
while (!q.isEmpty()) {
|
|
|
|
let node = q.dequeue();
|
2022-05-07 16:54:51 +02:00
|
|
|
if (!node) {
|
2022-04-27 21:37:58 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-05-05 20:28:58 +02:00
|
|
|
let [value, rank] = [node.value, node.rank];
|
2022-05-15 22:28:42 +02:00
|
|
|
let highlight =
|
|
|
|
node === highlightedNode ? ', color="blue", penwidth=3' : "";
|
|
|
|
result.push(
|
2022-05-15 22:46:55 +02:00
|
|
|
`\t"Node(${value})" [label="${value}, ${rank}"${highlight}];\n`
|
2022-05-15 22:28:42 +02:00
|
|
|
);
|
2022-04-27 21:37:58 +02:00
|
|
|
|
2022-05-01 17:10:24 +02:00
|
|
|
[node.left, node.right]
|
|
|
|
.filter((child) => child)
|
|
|
|
.forEach((child) => {
|
|
|
|
edges.push([node, child]);
|
|
|
|
q.enqueue(child);
|
|
|
|
});
|
2022-04-27 21:37:58 +02:00
|
|
|
}
|
|
|
|
|
2022-05-01 17:10:24 +02:00
|
|
|
result.push("");
|
|
|
|
|
|
|
|
edges.forEach((vertices) => {
|
2022-04-27 21:37:58 +02:00
|
|
|
let [u, v] = vertices;
|
2022-05-01 17:10:24 +02:00
|
|
|
result.push(
|
2022-05-05 20:28:58 +02:00
|
|
|
`\t"Node(${u.value})" -> "Node(${
|
|
|
|
v.value
|
2022-05-15 22:46:55 +02:00
|
|
|
})" [label="${nodeDifference(v)}"];\n`
|
2022-05-01 17:10:24 +02:00
|
|
|
);
|
2022-04-27 21:37:58 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-05-15 22:28:42 +02:00
|
|
|
toDot(highlightedNode) {
|
2022-05-15 22:46:55 +02:00
|
|
|
return [
|
|
|
|
"digraph {\n",
|
|
|
|
...this._getUnwrappedGraph(highlightedNode),
|
|
|
|
"}\n",
|
|
|
|
];
|
2022-05-01 17:10:24 +02:00
|
|
|
}
|
|
|
|
|
2022-04-27 21:37:58 +02:00
|
|
|
toString() {
|
2022-05-01 17:10:24 +02:00
|
|
|
return this.toDot().join("\n");
|
|
|
|
}
|
|
|
|
|
2022-05-15 22:28:42 +02:00
|
|
|
record(message, highlightedNode) {
|
2022-05-01 17:10:24 +02:00
|
|
|
if (!this.recorder) {
|
|
|
|
return;
|
|
|
|
}
|
2022-05-15 22:28:42 +02:00
|
|
|
this.recorder.record(this, message, highlightedNode);
|
2022-04-27 21:37:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2022-05-15 22:28:42 +02:00
|
|
|
this.record("Inserting key to the root", insertedNode);
|
2022-04-27 21:37:58 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let parent = findParentNode(value, this.root);
|
|
|
|
if (!parent) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
insertedNode.parent = parent;
|
|
|
|
|
|
|
|
if (value < parent.value) {
|
|
|
|
parent.left = insertedNode;
|
|
|
|
} else {
|
|
|
|
parent.right = insertedNode;
|
|
|
|
}
|
2022-05-15 22:28:42 +02:00
|
|
|
this.record("Inserting key", insertedNode);
|
2022-04-27 21:37:58 +02:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2022-05-06 09:40:51 +02:00
|
|
|
node.value = successor.value;
|
|
|
|
successor.value = null;
|
2022-05-15 17:53:12 +02:00
|
|
|
this.record(
|
2022-05-15 22:28:42 +02:00
|
|
|
"Replacing the value of deleted node with successor value",
|
|
|
|
node
|
2022-05-15 17:53:12 +02:00
|
|
|
);
|
2022-04-27 21:37:58 +02:00
|
|
|
|
2022-05-06 09:40:51 +02:00
|
|
|
return this.deleteNode(successor);
|
2022-04-27 21:37:58 +02:00
|
|
|
}
|
|
|
|
|
2022-05-15 22:28:42 +02:00
|
|
|
this.record("Replacing the node with one of its children", node);
|
2022-05-01 17:10:24 +02:00
|
|
|
|
2022-04-27 21:37:58 +02:00
|
|
|
return [y, parent];
|
|
|
|
}
|
|
|
|
|
|
|
|
delete(value) {
|
|
|
|
let node = this.root;
|
|
|
|
while (node && node.value != value) {
|
2022-05-01 17:09:46 +02:00
|
|
|
node = value < node.value ? node.left : node.right;
|
2022-04-27 21:37:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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!";
|
|
|
|
}
|
2022-05-01 17:09:46 +02:00
|
|
|
}
|