Skip to content

Commit f2fccce

Browse files
authored
feat: add solutions to lc problem: No.1206 (#4100)
No.1206.Design Skiplist
1 parent 3a82e38 commit f2fccce

File tree

3 files changed

+309
-2
lines changed

3 files changed

+309
-2
lines changed

solution/1200-1299/1206.Design Skiplist/README.md

+109-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,21 @@ skiplist.search(1); // 返回 false,1 已被擦除
8080

8181
### 方法一:数据结构
8282

83-
因为节点 `level` 随机,所以需要多个 `next` 指针,其余操作类似单链表。
83+
跳表的核心思想是使用多个“层级”来存储数据,每一层都相当于一个索引。数据从底层的链表开始逐渐上升到更高层级的链表,最终形成一个多层链表结构。每一层的节点只包含部分数据,这样就可以通过跳跃来减少查找的时间。
84+
85+
在这个问题中,我们使用一个 $\textit{Node}$ 类来表示跳表的节点,每个节点包含一个 $\textit{val}$ 域和一个 $\textit{next}$ 数组,数组的长度为 $\textit{level}$,表示当前节点在每一层的下一个节点。我们使用一个 $\textit{Skiplist}$ 类来实现跳表的操作。
86+
87+
跳表包含一个头节点 $\textit{head}$ 和当前的最大层数 $\textit{level}$。头节点的值设为 $-1$,用于标识链表的起始位置。我们使用一个动态数组 $\textit{next}$ 来存储指向后继节点的指针。
88+
89+
对于 $\textit{search}$ 操作,我们从跳表的最高层开始,逐层向下遍历,直到找到目标节点或者确定目标节点不存在。每层都通过 $\textit{find\_closest}$ 方法跳跃到最接近目标的节点。
90+
91+
对于 $\textit{add}$ 操作,我们首先随机决定新节点的层数。然后,从最高层开始,逐层找到每层中最接近新值的节点,并在相应位置插入新节点。如果插入的层数大于当前跳表的最大层数,我们需要更新跳表的层数。
92+
93+
对于 $\textit{erase}$ 操作,类似于查找操作,遍历跳表的每一层,找到需要删除的节点并删除它。删除节点时需要更新每一层的 $\textit{next}$ 指针。如果跳表的最高层没有节点,则需要减少跳表的层数。
94+
95+
另外,我们定义了一个 $\textit{random\_level}$ 方法来随机决定新节点的层数。该方法会生成一个 $[1, \textit{max\_level}]$ 之间的随机数,直到生成的随机数大于等于 $\textit{p}$ 为止。还有一个 $\textit{find\_closest}$ 方法用于查找每一层中最接近目标值的节点。
96+
97+
上述操作的时间复杂度为 $O(\log n)$,其中 $n$ 为跳表的节点数。空间复杂度为 $O(n)$。
8498

8599
<!-- tabs:start -->
86100

@@ -424,6 +438,100 @@ func randomLevel() int {
424438
*/
425439
```
426440

441+
#### TypeScript
442+
443+
```ts
444+
class Node {
445+
val: number;
446+
next: (Node | null)[];
447+
448+
constructor(val: number, level: number) {
449+
this.val = val;
450+
this.next = Array(level).fill(null);
451+
}
452+
}
453+
454+
class Skiplist {
455+
private static maxLevel: number = 32;
456+
private static p: number = 0.25;
457+
private head: Node;
458+
private level: number;
459+
460+
constructor() {
461+
this.head = new Node(-1, Skiplist.maxLevel);
462+
this.level = 0;
463+
}
464+
465+
search(target: number): boolean {
466+
let curr = this.head;
467+
for (let i = this.level - 1; i >= 0; i--) {
468+
curr = this.findClosest(curr, i, target);
469+
if (curr.next[i] && curr.next[i]!.val === target) {
470+
return true;
471+
}
472+
}
473+
return false;
474+
}
475+
476+
add(num: number): void {
477+
let curr = this.head;
478+
const level = this.randomLevel();
479+
const node = new Node(num, level);
480+
this.level = Math.max(this.level, level);
481+
482+
for (let i = this.level - 1; i >= 0; i--) {
483+
curr = this.findClosest(curr, i, num);
484+
if (i < level) {
485+
node.next[i] = curr.next[i];
486+
curr.next[i] = node;
487+
}
488+
}
489+
}
490+
491+
erase(num: number): boolean {
492+
let curr = this.head;
493+
let ok = false;
494+
495+
for (let i = this.level - 1; i >= 0; i--) {
496+
curr = this.findClosest(curr, i, num);
497+
if (curr.next[i] && curr.next[i]!.val === num) {
498+
curr.next[i] = curr.next[i]!.next[i];
499+
ok = true;
500+
}
501+
}
502+
503+
while (this.level > 1 && this.head.next[this.level - 1] === null) {
504+
this.level--;
505+
}
506+
507+
return ok;
508+
}
509+
510+
private findClosest(curr: Node, level: number, target: number): Node {
511+
while (curr.next[level] && curr.next[level]!.val < target) {
512+
curr = curr.next[level]!;
513+
}
514+
return curr;
515+
}
516+
517+
private randomLevel(): number {
518+
let level = 1;
519+
while (level < Skiplist.maxLevel && Math.random() < Skiplist.p) {
520+
level++;
521+
}
522+
return level;
523+
}
524+
}
525+
526+
/**
527+
* Your Skiplist object will be instantiated and called as such:
528+
* var obj = new Skiplist()
529+
* var param_1 = obj.search(target)
530+
* obj.add(num)
531+
* var param_3 = obj.erase(num)
532+
*/
533+
```
534+
427535
<!-- tabs:end -->
428536

429537
<!-- solution:end -->

solution/1200-1299/1206.Design Skiplist/README_EN.md

+111-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,23 @@ skiplist.search(1); // return False, 1 has already been erased.</pre>
7777

7878
<!-- solution:start -->
7979

80-
### Solution 1
80+
### Solution 1: Data Structure
81+
82+
The core idea of a skip list is to use multiple "levels" to store data, with each level acting as an index. Data starts from the bottom level linked list and gradually rises to higher levels, eventually forming a multi-level linked list structure. Each level's nodes only contain part of the data, allowing for jumps to reduce search time.
83+
84+
In this problem, we use a $\textit{Node}$ class to represent the nodes of the skip list. Each node contains a $\textit{val}$ field and a $\textit{next}$ array. The length of the array is $\textit{level}$, indicating the next node at each level. We use a $\textit{Skiplist}$ class to implement the skip list operations.
85+
86+
The skip list contains a head node $\textit{head}$ and the current maximum level $\textit{level}$. The head node's value is set to $-1$ to mark the starting position of the list. We use a dynamic array $\textit{next}$ to store pointers to successor nodes.
87+
88+
For the $\textit{search}$ operation, we start from the highest level of the skip list and traverse downwards until we find the target node or determine that the target node does not exist. At each level, we use the $\textit{find\_closest}$ method to jump to the node closest to the target.
89+
90+
For the $\textit{add}$ operation, we first randomly decide the level of the new node. Then, starting from the highest level, we find the node closest to the new value at each level and insert the new node at the appropriate position. If the level of the inserted node is greater than the current maximum level of the skip list, we need to update the level of the skip list.
91+
92+
For the $\textit{erase}$ operation, similar to the search operation, we traverse each level of the skip list to find and delete the target node. When deleting a node, we need to update the $\textit{next}$ pointers at each level. If the highest level of the skip list has no nodes, we need to decrease the level of the skip list.
93+
94+
Additionally, we define a $\textit{random\_level}$ method to randomly decide the level of the new node. This method generates a random number between $[1, \textit{max\_level}]$ until the generated random number is greater than or equal to $\textit{p}$. We also have a $\textit{find\_closest}$ method to find the node closest to the target value at each level.
95+
96+
The time complexity of the above operations is $O(\log n)$, where $n$ is the number of nodes in the skip list. The space complexity is $O(n)$.
8197

8298
<!-- tabs:start -->
8399

@@ -421,6 +437,100 @@ func randomLevel() int {
421437
*/
422438
```
423439

440+
#### TypeScript
441+
442+
```ts
443+
class Node {
444+
val: number;
445+
next: (Node | null)[];
446+
447+
constructor(val: number, level: number) {
448+
this.val = val;
449+
this.next = Array(level).fill(null);
450+
}
451+
}
452+
453+
class Skiplist {
454+
private static maxLevel: number = 32;
455+
private static p: number = 0.25;
456+
private head: Node;
457+
private level: number;
458+
459+
constructor() {
460+
this.head = new Node(-1, Skiplist.maxLevel);
461+
this.level = 0;
462+
}
463+
464+
search(target: number): boolean {
465+
let curr = this.head;
466+
for (let i = this.level - 1; i >= 0; i--) {
467+
curr = this.findClosest(curr, i, target);
468+
if (curr.next[i] && curr.next[i]!.val === target) {
469+
return true;
470+
}
471+
}
472+
return false;
473+
}
474+
475+
add(num: number): void {
476+
let curr = this.head;
477+
const level = this.randomLevel();
478+
const node = new Node(num, level);
479+
this.level = Math.max(this.level, level);
480+
481+
for (let i = this.level - 1; i >= 0; i--) {
482+
curr = this.findClosest(curr, i, num);
483+
if (i < level) {
484+
node.next[i] = curr.next[i];
485+
curr.next[i] = node;
486+
}
487+
}
488+
}
489+
490+
erase(num: number): boolean {
491+
let curr = this.head;
492+
let ok = false;
493+
494+
for (let i = this.level - 1; i >= 0; i--) {
495+
curr = this.findClosest(curr, i, num);
496+
if (curr.next[i] && curr.next[i]!.val === num) {
497+
curr.next[i] = curr.next[i]!.next[i];
498+
ok = true;
499+
}
500+
}
501+
502+
while (this.level > 1 && this.head.next[this.level - 1] === null) {
503+
this.level--;
504+
}
505+
506+
return ok;
507+
}
508+
509+
private findClosest(curr: Node, level: number, target: number): Node {
510+
while (curr.next[level] && curr.next[level]!.val < target) {
511+
curr = curr.next[level]!;
512+
}
513+
return curr;
514+
}
515+
516+
private randomLevel(): number {
517+
let level = 1;
518+
while (level < Skiplist.maxLevel && Math.random() < Skiplist.p) {
519+
level++;
520+
}
521+
return level;
522+
}
523+
}
524+
525+
/**
526+
* Your Skiplist object will be instantiated and called as such:
527+
* var obj = new Skiplist()
528+
* var param_1 = obj.search(target)
529+
* obj.add(num)
530+
* var param_3 = obj.erase(num)
531+
*/
532+
```
533+
424534
<!-- tabs:end -->
425535

426536
<!-- solution:end -->
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
class Node {
2+
val: number;
3+
next: (Node | null)[];
4+
5+
constructor(val: number, level: number) {
6+
this.val = val;
7+
this.next = Array(level).fill(null);
8+
}
9+
}
10+
11+
class Skiplist {
12+
private static maxLevel: number = 32;
13+
private static p: number = 0.25;
14+
private head: Node;
15+
private level: number;
16+
17+
constructor() {
18+
this.head = new Node(-1, Skiplist.maxLevel);
19+
this.level = 0;
20+
}
21+
22+
search(target: number): boolean {
23+
let curr = this.head;
24+
for (let i = this.level - 1; i >= 0; i--) {
25+
curr = this.findClosest(curr, i, target);
26+
if (curr.next[i] && curr.next[i]!.val === target) {
27+
return true;
28+
}
29+
}
30+
return false;
31+
}
32+
33+
add(num: number): void {
34+
let curr = this.head;
35+
const level = this.randomLevel();
36+
const node = new Node(num, level);
37+
this.level = Math.max(this.level, level);
38+
39+
for (let i = this.level - 1; i >= 0; i--) {
40+
curr = this.findClosest(curr, i, num);
41+
if (i < level) {
42+
node.next[i] = curr.next[i];
43+
curr.next[i] = node;
44+
}
45+
}
46+
}
47+
48+
erase(num: number): boolean {
49+
let curr = this.head;
50+
let ok = false;
51+
52+
for (let i = this.level - 1; i >= 0; i--) {
53+
curr = this.findClosest(curr, i, num);
54+
if (curr.next[i] && curr.next[i]!.val === num) {
55+
curr.next[i] = curr.next[i]!.next[i];
56+
ok = true;
57+
}
58+
}
59+
60+
while (this.level > 1 && this.head.next[this.level - 1] === null) {
61+
this.level--;
62+
}
63+
64+
return ok;
65+
}
66+
67+
private findClosest(curr: Node, level: number, target: number): Node {
68+
while (curr.next[level] && curr.next[level]!.val < target) {
69+
curr = curr.next[level]!;
70+
}
71+
return curr;
72+
}
73+
74+
private randomLevel(): number {
75+
let level = 1;
76+
while (level < Skiplist.maxLevel && Math.random() < Skiplist.p) {
77+
level++;
78+
}
79+
return level;
80+
}
81+
}
82+
83+
/**
84+
* Your Skiplist object will be instantiated and called as such:
85+
* var obj = new Skiplist()
86+
* var param_1 = obj.search(target)
87+
* obj.add(num)
88+
* var param_3 = obj.erase(num)
89+
*/

0 commit comments

Comments
 (0)