1 | import { SkipListNode } from "./SkipListNode";
|
2 | export class SkipList {
|
3 | constructor(compareKey) {
|
4 | this.compareKey = compareKey;
|
5 | this.level = 0;
|
6 | this.count = 0;
|
7 | this.head = new SkipListNode();
|
8 | }
|
9 | get Level() {
|
10 | return this.level;
|
11 | }
|
12 | get Count() {
|
13 | return this.count;
|
14 | }
|
15 | get Head() {
|
16 | return this.head;
|
17 | }
|
18 | isEmpty() {
|
19 | return this.count === 0;
|
20 | }
|
21 | randomLevel() {
|
22 | let k = 0;
|
23 | let random = parseInt((Math.random() * 10).toString(), 10);
|
24 | while (random % 2 === 0) {
|
25 | k++;
|
26 | random = parseInt((Math.random() * 10).toString(), 10);
|
27 | }
|
28 | return k > this.level ? this.level : k;
|
29 | }
|
30 | findNode(item) {
|
31 | let result = null;
|
32 | let temp = this.head;
|
33 | for (let i = this.level - 1; i >= 0; i--) {
|
34 | while (temp.getNext(i) && this.compare(temp.getNext(i).getItem(), this.compareKey ? { [this.compareKey]: item } : item)) {
|
35 | temp = temp.getNext(i);
|
36 | }
|
37 | }
|
38 | if (!temp.getNext(0)) {
|
39 | return result;
|
40 | }
|
41 | let isEqual = false;
|
42 | if (this.compareKey) {
|
43 | isEqual = temp.getNext(0).getItem()[this.compareKey] === item;
|
44 | }
|
45 | else {
|
46 | isEqual = temp.getNext(0).getItem() === item;
|
47 | }
|
48 | if (isEqual) {
|
49 | result = temp.getNext(0);
|
50 | }
|
51 | return result;
|
52 | }
|
53 | insert(item) {
|
54 | const updateNodes = this.findUpdateNodes(item);
|
55 | if (updateNodes[0] && updateNodes[0].getNext(0) && updateNodes[0].getNext(0).getItem() === item) {
|
56 | return this;
|
57 | }
|
58 | const level = this.randomLevel();
|
59 | if (level === this.level) {
|
60 | updateNodes[level] = this.head;
|
61 | this.level++;
|
62 | }
|
63 | this.insertNode(new SkipListNode(item), updateNodes, level);
|
64 | this.count++;
|
65 | return this;
|
66 | }
|
67 | remove(item) {
|
68 | const node = this.findNode(item);
|
69 | if (node) {
|
70 | const height = node.getHeight();
|
71 | for (let i = 0; i < height; i++) {
|
72 | const prev = node.getPrev(i);
|
73 | const next = node.getNext(i);
|
74 | prev.setNext(i, next);
|
75 | if (next) {
|
76 | next.setPrev(i, prev);
|
77 | }
|
78 | }
|
79 | while (this.level && !this.head.getNext(this.level - 1)) {
|
80 | this.head.deleteLastLevel();
|
81 | this.level--;
|
82 | }
|
83 | this.count--;
|
84 | }
|
85 | return this;
|
86 | }
|
87 | getSkipTables() {
|
88 | const table = [];
|
89 | for (let index = 0; index < this.level; index++) {
|
90 | const levelTables = [];
|
91 | let temp = this.head;
|
92 | while (temp = temp.getNext(index)) {
|
93 | levelTables.push(temp);
|
94 | }
|
95 | table[index] = levelTables;
|
96 | }
|
97 | return table;
|
98 | }
|
99 | toString() {
|
100 | const tables = this.getSkipTables();
|
101 | return tables.reverse().reduce((ori, item) => {
|
102 | ori.push(item.map(node => node.getItem().toString()).toString());
|
103 | return ori;
|
104 | }, []).join("\n");
|
105 | }
|
106 | compare(a, b) {
|
107 | if (this.compareKey) {
|
108 | return a[this.compareKey] < b[this.compareKey];
|
109 | }
|
110 | return a < b;
|
111 | }
|
112 | findUpdateNodes(item) {
|
113 | const updateNodes = [];
|
114 | for (let i = this.level - 1; i >= 0; i--) {
|
115 | let tempNode = this.head.getNext(i);
|
116 | let prevNode = null;
|
117 | while (tempNode && this.compare(tempNode.getItem(), item)) {
|
118 | prevNode = tempNode;
|
119 | tempNode = tempNode.getNext(i);
|
120 | }
|
121 | if (tempNode) {
|
122 | updateNodes[i] = tempNode.getPrev(i);
|
123 | }
|
124 | else {
|
125 | updateNodes[i] = prevNode;
|
126 | }
|
127 | }
|
128 | return updateNodes;
|
129 | }
|
130 | insertNode(node, updateNodes, level) {
|
131 | for (let i = level; i >= 0; i--) {
|
132 | const nextTemp = updateNodes[i].getNext(i);
|
133 | if (nextTemp) {
|
134 | nextTemp.setPrev(i, node);
|
135 | node.setNext(i, nextTemp);
|
136 | }
|
137 | updateNodes[i].setNext(i, node);
|
138 | node.setPrev(i, updateNodes[i]);
|
139 | }
|
140 | }
|
141 | }
|