class HashTable {
constructor(size = 0, lambda_min = 0.5, lambda_max = 0.75, size_min = 4) {
this.table = new Array(Math.max(size, size_min));
for (let i = 0; i < this.table.length; i++) this.table[i] = "E";
this.n = 0;
this.deleted = 0;
this.h = makeHash(this.m);
this.g = makeHash2(this.m);
Object.assign(this, { lambda_min, lambda_max, size_min });
}
get m() {
return this.table.length;
}
get lambda() {
return this.n / this.m;
}
find(key) {
let i = this.h(key);
let cell = this.table[i];
let j = this.g(key);
while (cell !== "E" && (cell === "D" || cell.key != key)) {
i = (i + j) % this.m;
cell = this.table[i];
}
if (cell !== "E" && cell !== "D") return cell.value;
return null;
}
insert(key, value) {
let i = this.h(key);
let cell = this.table[i];
let j = this.g(key);
while (cell !== "E" && cell !== "D") {
if (cell.key == key) throw `${key} already in table`;
i = (i + j) % this.m;
cell = this.table[i];
}
this.n++;
this.table[i] = { key, value };
if (this.lambda > this.lambda_max) this.rehash();
}
delete(key) {
let i = this.h(key);
let cell = this.table[i];
let j = this.g(key);
while (cell !== "E") {
if (cell !== "D" && cell.key == key) break;
i = (i + j) % this.m;
cell = this.table[i];
}
if (cell == "E") throw `${key} not in table`;
this.n--;
this.deleted++;
this.table[i] = "D";
if (
this.lambda < this.lambda_min ||
this.deleted / this.m >= 1 - this.lambda
)
this.rehash();
}
*cells() {
for (let cell of this.table) {
if (cell != "D" && cell != "E") yield cell;
}
}
rehash() {
const lambda_0 = (this.lambda_max + this.lambda_min) / 2;
let newSize = Math.max(this.size_min, Math.round(this.n / lambda_0));
if (newSize == this.m) return;
const newTable = new HashTable(
newSize,
this.lambda_min,
this.lambda_max,
this.size_min
);
for (let cell of this.cells()) {
newTable.insert(cell.key, cell.value);
}
Object.assign(this, newTable);
}
}