Tree = {
class Splitter {
constructor(key, left, right) {
Object.assign(this, { key, left, right });
}
find(key) {
if (key <= this.key) return this.left.find(key);
return this.right.find(key);
}
insert(key, value) {
if (key <= this.key) {
this.left = this.left.insert(key, value);
return this;
} else {
this.right = this.right.insert(key, value);
return this;
}
}
insertSG(key, value) {
let level;
if (key <= this.key)
{
[this.left, level] = this.left.insertSG(key, value);
}
else
{
[this.right, level] = this.right.insertSG(key, value);
}
return [this, level + 1];
}
scapegoat(key) {
if (key <= this.key) {
if (3 * this.left.size() > 2 * this.size())
{
return [this.size(), this];
}
return this.left.scapegoat(key);
}
if (3 * this.right.size() > 2 * this.size())
{
return [this.size(), this];
}
return this.right.scapegoat(key);
}
remove(key) {
if (key <= this.key) {
this.left = this.left.remove(key);
if (this.left == null) return this.right;
} else {
this.right = this.right.remove(key);
if (this.right == null) return this.left;
}
return this;
}
inorder(array) {
this.left.inorder(array);
this.right.inorder(array);
}
rebuild() {
let x = [];
this.inorder(x);
function buildSubtree(i, k) {
if (k == 1)
{
return x[i];
}
let n = k >> 1;
return new Splitter(x[i + n - 1].key, buildSubtree(i, n), buildSubtree(i + n, k - n));
}
let newtree = buildSubtree(0, x.length);
Object.assign(this, newtree);
}
size() {
return this.left.size() + this.right.size();
}
}
class External {
constructor(key, value) {
Object.assign(this, { key, value });
}
find(key) {
if (key == this.key) return this.value;
return null;
}
insert(key, value) {
if (key == this.key) throw "Key already in tree";
if (key < this.key) {
return new Splitter(key, new External(key, value), this);
} else {
return new Splitter(this.key, this, new External(key, value));
}
}
insertSG(key, value) {
let result = this.insert(key, value);
return [result, 1];
}
scapegoat(key) {
return [1, null];
}
remove(key) {
if (this.key == key) return null;
throw "Key not found " + key + " here is " + this.key;
}
inorder(array) {
array.push(this);
}
size() {
return 1;
}
}
class XBST {
constructor() {
this.root = null;
this.n = this.m = 0;
}
find(key) {
if (this.root == null) return null;
return this.root.find(key);
}
insert(key, value) {
if (this.root == null) {
this.root = new External(key, value);
} else {
this.root = this.root.insert(key, value);
}
}
insertSG(key, value) {
let depth;
if (this.root == null) [this.root, depth] = [new External(key, value), 0];
else [this.root, depth] = this.root.insertSG(key, value);
this.n++;
this.m++;
let limit = Math.log(this.m) / Math.log(1.5);
if (depth > limit) {
let [size, sg] = this.root.scapegoat(key);
sg.rebuild();
}
return [depth, limit];
}
remove(key) {
if (this.root == null) throw "Key does not exist in this tree";
this.root = this.root.remove(key);
}
removeSG(key) {
this.remove(key);
this.n--;
if (this.m > this.n * 2) {
this.rebuild();
this.m = this.n;
}
}
inorder() {
if (this.root) {
let result = [];
this.root.inorder(result);
return result;
}
return [];
}
rebuild() {
let A = this.inorder();
function buildSubtree(i, k) {
if (k == 1) return A[i];
let n = k >> 1;
return new Splitter(
A[i + n - 1].key,
buildSubtree(i, n),
buildSubtree(i + n, k - n)
);
}
if (this.root) this.root = buildSubtree(0, A.length);
}
graphviz() {
if (this.root == null) {
return `graph {}`;
}
let count = 0;
let nodeMap = new Map();
let result = "";
let nullNode = () => {
let nodeId = "node" + ++count;
result += `${nodeId} [label="" shape=point width=0];`;
return nodeId;
};
let getNodeId = node => {
if (nodeMap.has(node)) return nodeMap.get(node);
let nodeId = "node" + ++count;
nodeMap.set(node, nodeId);
if (node instanceof External) {
result += `${nodeId} [label="${node.key}|${node.value}" shape=record];`;
} else {
result += `${nodeId} [label="${node.key}"];`;
}
return nodeId;
};
let traverse = node => {
let nodeId = getNodeId(node);
if (node instanceof External) return;
let [left, middle, right] = [
getNodeId(node.left),
nullNode(),
getNodeId(node.right)
];
result += `${nodeId} -- ${left};\n`;
result += `${nodeId} -- ${middle} [style=invis];\n`;
result += `${nodeId} -- ${right};\n`;
traverse(node.left);
traverse(node.right);
};
traverse(this.root);
return `graph{${result}}`;
}
}
return XBST;
}