Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a4dc327

Browse files
author
name
committedOct 5, 2022
skip-list-implementation
1 parent a2226c1 commit a4dc327

File tree

2 files changed

+372
-0
lines changed

2 files changed

+372
-0
lines changed
 
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
In computer science, a skip list is a probabilistic data structure that allows {\mathcal {O}} search complexity as well as {\mathcal {O}} insertion complexity within an ordered sequence of n elements.
+371
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,371 @@
1+
const DEFAULT_STACK_UP_PROBABILITY = 0.25;
2+
3+
class ProperSkipList {
4+
constructor(options) {
5+
options = options || {};
6+
this.stackUpProbability = options.stackUpProbability || DEFAULT_STACK_UP_PROBABILITY;
7+
this.updateLength = options.updateLength !== false;
8+
this.typePriorityMap = {
9+
'undefined': 0,
10+
'object': 1,
11+
'number': 2,
12+
'bigint': 2,
13+
'string': 3
14+
};
15+
this.clear();
16+
}
17+
18+
clear() {
19+
let headNode = {
20+
prev: null
21+
};
22+
let tailNode = {
23+
next: null
24+
};
25+
this.head = {
26+
isHead: true,
27+
key: undefined,
28+
value: undefined,
29+
nodes: [headNode]
30+
};
31+
this.tail = {
32+
isTail: true,
33+
key: undefined,
34+
value: undefined,
35+
nodes: [tailNode]
36+
};
37+
headNode.next = tailNode;
38+
tailNode.prev = headNode;
39+
headNode.group = this.head;
40+
tailNode.group = this.tail;
41+
this.length = this.updateLength ? 0 : undefined;
42+
}
43+
44+
upsert(key, value) {
45+
let {matchingNode, prevNode, searchPath} = this._searchAndTrack(key);
46+
if (matchingNode) {
47+
let previousValue = matchingNode.group.value;
48+
matchingNode.group.value = value;
49+
return previousValue;
50+
}
51+
52+
// Insert the entry.
53+
let newNode = {
54+
prev: prevNode,
55+
next: prevNode.next
56+
};
57+
let newGroup = {
58+
key,
59+
value,
60+
nodes: [newNode]
61+
};
62+
newNode.group = newGroup;
63+
prevNode.next = newNode;
64+
newNode.next.prev = newNode;
65+
66+
// Stack up the entry for fast search.
67+
let layerIndex = 1;
68+
while (Math.random() < this.stackUpProbability) {
69+
let prevLayerNode = searchPath[layerIndex];
70+
if (!prevLayerNode) {
71+
let newHeadNode = {
72+
prev: null,
73+
group: this.head
74+
};
75+
let newTailNode = {
76+
next: null,
77+
group: this.tail
78+
};
79+
newHeadNode.next = newTailNode;
80+
this.head.nodes.push(newHeadNode);
81+
newTailNode.prev = newHeadNode;
82+
this.tail.nodes.push(newTailNode);
83+
prevLayerNode = newHeadNode;
84+
}
85+
let newNode = {
86+
prev: prevLayerNode,
87+
next: prevLayerNode.next,
88+
group: newGroup
89+
};
90+
prevLayerNode.next = newNode;
91+
newNode.next.prev = newNode;
92+
newGroup.nodes.push(newNode);
93+
layerIndex++;
94+
}
95+
if (this.updateLength) this.length++;
96+
97+
return undefined;
98+
}
99+
100+
find(key) {
101+
let {matchingNode} = this._search(key);
102+
return matchingNode ? matchingNode.group.value : undefined;
103+
}
104+
105+
has(key) {
106+
return !!this.find(key);
107+
}
108+
109+
_isAGreaterThanB(a, b) {
110+
let typeA = typeof a;
111+
let typeB = typeof b;
112+
if (typeA === typeB) {
113+
return a > b;
114+
}
115+
let typeAPriority = this.typePriorityMap[typeA];
116+
let typeBPriority = this.typePriorityMap[typeB];
117+
if (typeAPriority === typeBPriority) {
118+
return a > b;
119+
}
120+
return typeAPriority > typeBPriority;
121+
}
122+
123+
// The two search methods are similar but were separated for performance reasons.
124+
_searchAndTrack(key) {
125+
let layerCount = this.head.nodes.length;
126+
let searchPath = new Array(layerCount);
127+
let layerIndex = layerCount - 1;
128+
let currentNode = this.head.nodes[layerIndex];
129+
let prevNode = currentNode;
130+
131+
while (true) {
132+
let currentNodeGroup = currentNode.group;
133+
let currentKey = currentNodeGroup.key;
134+
if (!currentNodeGroup.isTail) {
135+
if (this._isAGreaterThanB(key, currentKey) || currentNodeGroup.isHead) {
136+
prevNode = currentNode;
137+
currentNode = currentNode.next;
138+
continue;
139+
}
140+
if (key === currentKey) {
141+
let matchingNode = currentNodeGroup.nodes[0];
142+
searchPath[layerIndex] = matchingNode;
143+
return {matchingNode, prevNode: matchingNode.prev, searchPath};
144+
}
145+
}
146+
searchPath[layerIndex] = prevNode;
147+
if (--layerIndex < 0) {
148+
return {matchingNode: undefined, prevNode, searchPath};
149+
}
150+
currentNode = prevNode.group.nodes[layerIndex];
151+
}
152+
}
153+
154+
_search(key) {
155+
let layerIndex = this.head.nodes.length - 1;
156+
let currentNode = this.head.nodes[layerIndex];
157+
let prevNode = currentNode;
158+
while (true) {
159+
let currentNodeGroup = currentNode.group;
160+
let currentKey = currentNodeGroup.key;
161+
if (!currentNodeGroup.isTail) {
162+
if (this._isAGreaterThanB(key, currentKey) || currentNodeGroup.isHead) {
163+
prevNode = currentNode;
164+
currentNode = currentNode.next;
165+
continue;
166+
}
167+
if (key === currentKey) {
168+
let matchingNode = currentNodeGroup.nodes[0];
169+
return {matchingNode, prevNode: matchingNode.prev};
170+
}
171+
}
172+
if (--layerIndex < 0) {
173+
return {matchingNode: undefined, prevNode};
174+
}
175+
currentNode = prevNode.group.nodes[layerIndex];
176+
}
177+
}
178+
179+
findEntriesFromMin() {
180+
return this._createEntriesIteratorAsc(this.head.nodes[0].next);
181+
}
182+
183+
findEntriesFromMax() {
184+
return this._createEntriesIteratorDesc(this.tail.nodes[0].prev);
185+
}
186+
187+
minEntry() {
188+
let [key, value] = this.findEntriesFromMin().next().value;
189+
return [key, value];
190+
}
191+
192+
maxEntry() {
193+
let [key, value] = this.findEntriesFromMax().next().value;
194+
return [key, value];
195+
}
196+
197+
minKey() {
198+
return this.minEntry()[0];
199+
}
200+
201+
maxKey() {
202+
return this.maxEntry()[0];
203+
}
204+
205+
minValue() {
206+
return this.minEntry()[1];
207+
}
208+
209+
maxValue() {
210+
return this.maxEntry()[1];
211+
}
212+
213+
_extractNode(matchingNode) {
214+
let nodes = matchingNode.group.nodes;
215+
for (let layerNode of nodes) {
216+
let prevNode = layerNode.prev;
217+
prevNode.next = layerNode.next;
218+
prevNode.next.prev = prevNode;
219+
}
220+
if (this.updateLength) this.length--;
221+
return matchingNode.group.value;
222+
}
223+
224+
extract(key) {
225+
let {matchingNode} = this._search(key);
226+
if (matchingNode) {
227+
return this._extractNode(matchingNode);
228+
}
229+
return undefined;
230+
}
231+
232+
delete(key) {
233+
return this.extract(key) !== undefined;
234+
}
235+
236+
findEntries(fromKey) {
237+
let {matchingNode, prevNode} = this._search(fromKey);
238+
if (matchingNode) {
239+
return {
240+
matchingValue: matchingNode.group.value,
241+
asc: this._createEntriesIteratorAsc(matchingNode),
242+
desc: this._createEntriesIteratorDesc(matchingNode)
243+
};
244+
}
245+
return {
246+
matchingValue: undefined,
247+
asc: this._createEntriesIteratorAsc(prevNode.next),
248+
desc: this._createEntriesIteratorDesc(prevNode)
249+
};
250+
}
251+
252+
deleteRange(fromKey, toKey, deleteLeft, deleteRight) {
253+
if (fromKey == null) {
254+
fromKey = this.minKey();
255+
deleteLeft = true;
256+
}
257+
if (toKey == null) {
258+
toKey = this.maxKey();
259+
deleteRight = true;
260+
}
261+
if (this._isAGreaterThanB(fromKey, toKey)) {
262+
return;
263+
}
264+
let {prevNode: fromNode, searchPath: leftSearchPath, matchingNode: matchingLeftNode} = this._searchAndTrack(fromKey);
265+
let {prevNode: toNode, searchPath: rightSearchPath, matchingNode: matchingRightNode} = this._searchAndTrack(toKey);
266+
let leftNode = matchingLeftNode ? matchingLeftNode : fromNode;
267+
let rightNode = matchingRightNode ? matchingRightNode : toNode.next;
268+
269+
if (leftNode === rightNode) {
270+
if (deleteLeft) {
271+
this._extractNode(leftNode);
272+
}
273+
return;
274+
}
275+
276+
if (this.updateLength) {
277+
let currentNode = leftNode;
278+
while (currentNode && currentNode.next !== rightNode) {
279+
this.length--;
280+
currentNode = currentNode.next;
281+
}
282+
}
283+
284+
let leftGroupNodes = leftNode.group.nodes;
285+
let rightGroupNodes = rightNode.group.nodes;
286+
let layerCount = this.head.nodes.length;
287+
288+
for (let layerIndex = 0; layerIndex < layerCount; layerIndex++) {
289+
let layerLeftNode = leftGroupNodes[layerIndex];
290+
let layerRightNode = rightGroupNodes[layerIndex];
291+
292+
if (layerLeftNode && layerRightNode) {
293+
layerLeftNode.next = layerRightNode;
294+
layerRightNode.prev = layerLeftNode;
295+
continue;
296+
}
297+
if (layerLeftNode) {
298+
let layerRightmostNode = rightSearchPath[layerIndex];
299+
if (!layerRightmostNode.group.isTail) {
300+
layerRightmostNode = layerRightmostNode.next;
301+
}
302+
layerLeftNode.next = layerRightmostNode;
303+
layerRightmostNode.prev = layerLeftNode;
304+
continue;
305+
}
306+
if (layerRightNode) {
307+
let layerLeftmostNode = leftSearchPath[layerIndex];
308+
layerLeftmostNode.next = layerRightNode;
309+
layerRightNode.prev = layerLeftmostNode;
310+
continue;
311+
}
312+
// If neither left nor right nodes are present on the layer, connect based
313+
// on search path to remove in-between entries.
314+
let layerRightmostNode = rightSearchPath[layerIndex];
315+
if (!layerRightmostNode.group.isTail) {
316+
layerRightmostNode = layerRightmostNode.next;
317+
}
318+
let layerLeftmostNode = leftSearchPath[layerIndex];
319+
layerLeftmostNode.next = layerRightmostNode;
320+
layerRightmostNode.prev = layerLeftmostNode;
321+
}
322+
if (deleteLeft && matchingLeftNode) {
323+
this._extractNode(matchingLeftNode);
324+
}
325+
if (deleteRight && matchingRightNode) {
326+
this._extractNode(matchingRightNode);
327+
}
328+
}
329+
330+
_createEntriesIteratorAsc(currentNode) {
331+
let i = 0;
332+
return {
333+
next: function () {
334+
let currentGroup = currentNode.group;
335+
if (currentGroup.isTail) {
336+
return {
337+
value: [currentNode.key, currentNode.value, i],
338+
done: true
339+
}
340+
}
341+
currentNode = currentNode.next;
342+
return {
343+
value: [currentGroup.key, currentGroup.value, i++],
344+
done: currentGroup.isTail
345+
};
346+
},
347+
[Symbol.iterator]: function () { return this; }
348+
};
349+
}
350+
351+
_createEntriesIteratorDesc(currentNode) {
352+
let i = 0;
353+
return {
354+
next: function () {
355+
let currentGroup = currentNode.group;
356+
if (currentGroup.isHead) {
357+
return {
358+
value: [currentNode.key, currentNode.value, i],
359+
done: true
360+
}
361+
}
362+
currentNode = currentNode.prev;
363+
return {
364+
value: [currentGroup.key, currentGroup.value, i++],
365+
done: currentGroup.isHead
366+
};
367+
},
368+
[Symbol.iterator]: function () { return this; }
369+
};
370+
}
371+
}

0 commit comments

Comments
 (0)