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; } let [value, rank] = [node.value, node.rank]; result.push(`\t"Node(${value})" [label="${value}, ${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"Node(${u.value})" -> "Node(${ v.value })" [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!"; } }