ts-ds-tool
Version:
Data structure and algorithm of TypeScript
142 lines (141 loc) • 4.3 kB
JavaScript
import { SkipListNode } from "./SkipListNode";
export class SkipList {
constructor(compareKey) {
this.compareKey = compareKey;
this.level = 0;
this.count = 0;
this.head = new SkipListNode();
}
get Level() {
return this.level;
}
get Count() {
return this.count;
}
get Head() {
return this.head;
}
isEmpty() {
return this.count === 0;
}
randomLevel() {
let k = 0;
let random = parseInt((Math.random() * 10).toString(), 10);
while (random % 2 === 0) {
k++;
random = parseInt((Math.random() * 10).toString(), 10);
}
return k > this.level ? this.level : k;
}
findNode(item) {
let result = null;
let temp = this.head;
for (let i = this.level - 1; i >= 0; i--) {
while (temp.getNext(i) && this.compare(temp.getNext(i).getItem(), this.compareKey ? { [this.compareKey]: item } : item)) {
temp = temp.getNext(i);
}
}
if (!temp.getNext(0)) {
return result;
}
let isEqual = false;
if (this.compareKey) {
isEqual = temp.getNext(0).getItem()[this.compareKey] === item;
}
else {
isEqual = temp.getNext(0).getItem() === item;
}
if (isEqual) {
result = temp.getNext(0);
}
return result;
}
insert(item) {
const updateNodes = this.findUpdateNodes(item);
if (updateNodes[0] && updateNodes[0].getNext(0) && updateNodes[0].getNext(0).getItem() === item) {
return this;
}
const level = this.randomLevel();
if (level === this.level) {
updateNodes[level] = this.head;
this.level++;
}
this.insertNode(new SkipListNode(item), updateNodes, level);
this.count++;
return this;
}
remove(item) {
const node = this.findNode(item);
if (node) {
const height = node.getHeight();
for (let i = 0; i < height; i++) {
const prev = node.getPrev(i);
const next = node.getNext(i);
prev.setNext(i, next);
if (next) {
next.setPrev(i, prev);
}
}
while (this.level && !this.head.getNext(this.level - 1)) {
this.head.deleteLastLevel();
this.level--;
}
this.count--;
}
return this;
}
getSkipTables() {
const table = [];
for (let index = 0; index < this.level; index++) {
const levelTables = [];
let temp = this.head;
while (temp = temp.getNext(index)) {
levelTables.push(temp);
}
table[index] = levelTables;
}
return table;
}
toString() {
const tables = this.getSkipTables();
return tables.reverse().reduce((ori, item) => {
ori.push(item.map(node => node.getItem().toString()).toString());
return ori;
}, []).join("\n");
}
compare(a, b) {
if (this.compareKey) {
return a[this.compareKey] < b[this.compareKey];
}
return a < b;
}
findUpdateNodes(item) {
const updateNodes = [];
for (let i = this.level - 1; i >= 0; i--) {
let tempNode = this.head.getNext(i);
let prevNode = null;
while (tempNode && this.compare(tempNode.getItem(), item)) {
prevNode = tempNode;
tempNode = tempNode.getNext(i);
}
if (tempNode) {
updateNodes[i] = tempNode.getPrev(i);
}
else {
updateNodes[i] = prevNode;
}
}
return updateNodes;
}
insertNode(node, updateNodes, level) {
for (let i = level; i >= 0; i--) {
const nextTemp = updateNodes[i].getNext(i);
if (nextTemp) {
nextTemp.setPrev(i, node);
node.setNext(i, nextTemp);
}
updateNodes[i].setNext(i, node);
node.setPrev(i, updateNodes[i]);
}
}
}