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(highlightedNode) { let result = []; let q = new Queue(); q.enqueue(this.root); let edges = []; while (!q.isEmpty()) { let node = q.dequeue(); if (!node) { continue; } let [value, rank] = [node.value, node.rank]; let highlight = node === highlightedNode ? ', color="blue", penwidth=3' : ""; result.push( `\t"Node(${value})" [label="${value}, ${rank}"${highlight}];` ); [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"Node(${u.value})" -> "Node(${ v.value })" [label="${nodeDifference(v)}"]` ); }); return result; } toDot(highlightedNode) { return ["digraph {", ...this._getUnwrappedGraph(highlightedNode), "}"]; } toString() { return this.toDot().join("\n"); } record(message, highlightedNode) { if (!this.recorder) { return; } this.recorder.record(this, message, highlightedNode); } 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("Inserting key to the root", insertedNode); 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("Inserting key", insertedNode); 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); node.value = successor.value; successor.value = null; this.record( "Replacing the value of deleted node with successor value", node ); return this.deleteNode(successor); } this.record("Replacing the node with one of its children", node); 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!"; } }