1 | import { SkipListNode } from "./SkipListNode";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | export class SkipList<T>{
|
7 | private level = 0;
|
8 | private count = 0;
|
9 | private head: SkipListNode<T>;
|
10 | constructor(private compareKey?: keyof T){
|
11 | this.head = new SkipListNode();
|
12 | }
|
13 |
|
14 | |
15 |
|
16 |
|
17 | public get Level(){
|
18 | return this.level;
|
19 | }
|
20 |
|
21 | |
22 |
|
23 |
|
24 | public get Count(){
|
25 | return this.count;
|
26 | }
|
27 |
|
28 | |
29 |
|
30 |
|
31 | public get Head(){
|
32 | return this.head;
|
33 | }
|
34 |
|
35 | |
36 |
|
37 |
|
38 | public isEmpty(){
|
39 | return this.count === 0;
|
40 | }
|
41 |
|
42 | |
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 | private randomLevel(){
|
49 | let k = 0;
|
50 | let random = parseInt((Math.random() * 10).toString(), 10);
|
51 | while (random % 2 === 0){
|
52 | k++;
|
53 | random = parseInt((Math.random() * 10).toString(), 10);
|
54 | }
|
55 | return k > this.level ? this.level : k;
|
56 | }
|
57 |
|
58 | |
59 |
|
60 |
|
61 |
|
62 |
|
63 | public findNode(item: any): SkipListNode<T>{
|
64 | let result: SkipListNode<T> = null;
|
65 | let temp = this.head;
|
66 |
|
67 | for (let i = this.level - 1; i >= 0; i--){
|
68 |
|
69 | while (temp.getNext(i) && this.compare(temp.getNext(i).getItem() ,
|
70 | this.compareKey ? {[this.compareKey]: item} as any : item)){
|
71 | temp = temp.getNext(i);
|
72 | }
|
73 | }
|
74 | if (!temp.getNext(0)){
|
75 | return result;
|
76 | }
|
77 | let isEqual = false;
|
78 | if (this.compareKey) {
|
79 | isEqual = temp.getNext(0).getItem()[this.compareKey] === item;
|
80 | }else{
|
81 | isEqual = temp.getNext(0).getItem() === item;
|
82 | }
|
83 | if (isEqual){
|
84 | result = temp.getNext(0);
|
85 | }
|
86 | return result;
|
87 | }
|
88 |
|
89 | |
90 |
|
91 |
|
92 |
|
93 |
|
94 | public insert(item: T){
|
95 | |
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 | const updateNodes = this.findUpdateNodes(item);
|
102 | if (updateNodes[0] && updateNodes[0].getNext(0) && updateNodes[0].getNext(0).getItem() === item){
|
103 | return this;
|
104 | }
|
105 | const level = this.randomLevel();
|
106 | if (level === this.level){
|
107 | updateNodes[level] = this.head;
|
108 | this.level++;
|
109 | }
|
110 | this.insertNode(new SkipListNode(item), updateNodes, level);
|
111 | this.count++;
|
112 | return this;
|
113 | }
|
114 |
|
115 | |
116 |
|
117 |
|
118 |
|
119 | public remove(item: any){
|
120 | const node = this.findNode(item);
|
121 | if (node){
|
122 |
|
123 | const height = node.getHeight();
|
124 | for (let i = 0; i < height; i++){
|
125 | const prev = node.getPrev(i);
|
126 | const next = node.getNext(i);
|
127 | prev.setNext(i, next);
|
128 | if (next){
|
129 | next.setPrev(i, prev);
|
130 | }
|
131 | }
|
132 |
|
133 | while (this.level && !this.head.getNext(this.level - 1)){
|
134 | this.head.deleteLastLevel();
|
135 | this.level--;
|
136 | }
|
137 | this.count--;
|
138 | }
|
139 | return this;
|
140 | }
|
141 |
|
142 | |
143 |
|
144 |
|
145 |
|
146 | public getSkipTables(){
|
147 | const table = [];
|
148 | for (let index = 0; index < this.level; index++) {
|
149 | const levelTables: Array<SkipListNode<T>> = [];
|
150 | let temp = this.head;
|
151 |
|
152 | while (temp = temp.getNext(index)){
|
153 | levelTables.push(temp);
|
154 | }
|
155 | table[index] = levelTables;
|
156 | }
|
157 | return table;
|
158 | }
|
159 |
|
160 | public toString(){
|
161 | const tables = this.getSkipTables();
|
162 | return tables.reverse().reduce((ori: Array<string>, item) => {
|
163 | ori.push(item.map(node => node.getItem().toString()).toString());
|
164 | return ori;
|
165 | }, []).join("\n");
|
166 | }
|
167 |
|
168 | private compare(a: T, b: T){
|
169 | if (this.compareKey){
|
170 | return a[this.compareKey] < b[this.compareKey];
|
171 | }
|
172 | return a < b;
|
173 | }
|
174 |
|
175 | |
176 |
|
177 |
|
178 |
|
179 |
|
180 | private findUpdateNodes(item: T){
|
181 | const updateNodes: Array<SkipListNode<T>> = [];
|
182 |
|
183 | for (let i = this.level - 1; i >= 0; i--){
|
184 | let tempNode: SkipListNode<T> = this.head.getNext(i);
|
185 | let prevNode: SkipListNode<T> = null;
|
186 | while (tempNode && this.compare(tempNode.getItem() , item)){
|
187 | prevNode = tempNode;
|
188 | tempNode = tempNode.getNext(i);
|
189 | }
|
190 |
|
191 | if (tempNode){
|
192 | updateNodes[i] = tempNode.getPrev(i);
|
193 | }else{
|
194 | updateNodes[i] = prevNode;
|
195 | }
|
196 | }
|
197 | return updateNodes;
|
198 | }
|
199 |
|
200 | |
201 |
|
202 |
|
203 |
|
204 |
|
205 |
|
206 | private insertNode(node: SkipListNode<T>, updateNodes: Array<SkipListNode<T>>, level: number){
|
207 |
|
208 | for (let i = level; i >= 0; i--){
|
209 | const nextTemp = updateNodes[i].getNext(i);
|
210 | if (nextTemp){
|
211 | nextTemp.setPrev(i, node);
|
212 | node.setNext(i, nextTemp);
|
213 | }
|
214 | updateNodes[i].setNext(i, node);
|
215 | node.setPrev(i, updateNodes[i]);
|
216 | }
|
217 | }
|
218 | }
|