Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add solutions to lc problem: No.1206 #4100

Merged
merged 1 commit into from
Feb 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 109 additions & 1 deletion solution/1200-1299/1206.Design Skiplist/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,21 @@ skiplist.search(1); // 返回 false,1 已被擦除

### 方法一:数据结构

因为节点 `level` 随机,所以需要多个 `next` 指针,其余操作类似单链表。
跳表的核心思想是使用多个“层级”来存储数据,每一层都相当于一个索引。数据从底层的链表开始逐渐上升到更高层级的链表,最终形成一个多层链表结构。每一层的节点只包含部分数据,这样就可以通过跳跃来减少查找的时间。

在这个问题中,我们使用一个 $\textit{Node}$ 类来表示跳表的节点,每个节点包含一个 $\textit{val}$ 域和一个 $\textit{next}$ 数组,数组的长度为 $\textit{level}$,表示当前节点在每一层的下一个节点。我们使用一个 $\textit{Skiplist}$ 类来实现跳表的操作。

跳表包含一个头节点 $\textit{head}$ 和当前的最大层数 $\textit{level}$。头节点的值设为 $-1$,用于标识链表的起始位置。我们使用一个动态数组 $\textit{next}$ 来存储指向后继节点的指针。

对于 $\textit{search}$ 操作,我们从跳表的最高层开始,逐层向下遍历,直到找到目标节点或者确定目标节点不存在。每层都通过 $\textit{find\_closest}$ 方法跳跃到最接近目标的节点。

对于 $\textit{add}$ 操作,我们首先随机决定新节点的层数。然后,从最高层开始,逐层找到每层中最接近新值的节点,并在相应位置插入新节点。如果插入的层数大于当前跳表的最大层数,我们需要更新跳表的层数。

对于 $\textit{erase}$ 操作,类似于查找操作,遍历跳表的每一层,找到需要删除的节点并删除它。删除节点时需要更新每一层的 $\textit{next}$ 指针。如果跳表的最高层没有节点,则需要减少跳表的层数。

另外,我们定义了一个 $\textit{random\_level}$ 方法来随机决定新节点的层数。该方法会生成一个 $[1, \textit{max\_level}]$ 之间的随机数,直到生成的随机数大于等于 $\textit{p}$ 为止。还有一个 $\textit{find\_closest}$ 方法用于查找每一层中最接近目标值的节点。

上述操作的时间复杂度为 $O(\log n)$,其中 $n$ 为跳表的节点数。空间复杂度为 $O(n)$。

<!-- tabs:start -->

Expand Down Expand Up @@ -424,6 +438,100 @@ func randomLevel() int {
*/
```

#### TypeScript

```ts
class Node {
val: number;
next: (Node | null)[];

constructor(val: number, level: number) {
this.val = val;
this.next = Array(level).fill(null);
}
}

class Skiplist {
private static maxLevel: number = 32;
private static p: number = 0.25;
private head: Node;
private level: number;

constructor() {
this.head = new Node(-1, Skiplist.maxLevel);
this.level = 0;
}

search(target: number): boolean {
let curr = this.head;
for (let i = this.level - 1; i >= 0; i--) {
curr = this.findClosest(curr, i, target);
if (curr.next[i] && curr.next[i]!.val === target) {
return true;
}
}
return false;
}

add(num: number): void {
let curr = this.head;
const level = this.randomLevel();
const node = new Node(num, level);
this.level = Math.max(this.level, level);

for (let i = this.level - 1; i >= 0; i--) {
curr = this.findClosest(curr, i, num);
if (i < level) {
node.next[i] = curr.next[i];
curr.next[i] = node;
}
}
}

erase(num: number): boolean {
let curr = this.head;
let ok = false;

for (let i = this.level - 1; i >= 0; i--) {
curr = this.findClosest(curr, i, num);
if (curr.next[i] && curr.next[i]!.val === num) {
curr.next[i] = curr.next[i]!.next[i];
ok = true;
}
}

while (this.level > 1 && this.head.next[this.level - 1] === null) {
this.level--;
}

return ok;
}

private findClosest(curr: Node, level: number, target: number): Node {
while (curr.next[level] && curr.next[level]!.val < target) {
curr = curr.next[level]!;
}
return curr;
}

private randomLevel(): number {
let level = 1;
while (level < Skiplist.maxLevel && Math.random() < Skiplist.p) {
level++;
}
return level;
}
}

/**
* Your Skiplist object will be instantiated and called as such:
* var obj = new Skiplist()
* var param_1 = obj.search(target)
* obj.add(num)
* var param_3 = obj.erase(num)
*/
```

<!-- tabs:end -->

<!-- solution:end -->
Expand Down
112 changes: 111 additions & 1 deletion solution/1200-1299/1206.Design Skiplist/README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,23 @@ skiplist.search(1); // return False, 1 has already been erased.</pre>

<!-- solution:start -->

### Solution 1
### Solution 1: Data Structure

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.

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.

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.

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.

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.

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.

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.

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)$.

<!-- tabs:start -->

Expand Down Expand Up @@ -421,6 +437,100 @@ func randomLevel() int {
*/
```

#### TypeScript

```ts
class Node {
val: number;
next: (Node | null)[];

constructor(val: number, level: number) {
this.val = val;
this.next = Array(level).fill(null);
}
}

class Skiplist {
private static maxLevel: number = 32;
private static p: number = 0.25;
private head: Node;
private level: number;

constructor() {
this.head = new Node(-1, Skiplist.maxLevel);
this.level = 0;
}

search(target: number): boolean {
let curr = this.head;
for (let i = this.level - 1; i >= 0; i--) {
curr = this.findClosest(curr, i, target);
if (curr.next[i] && curr.next[i]!.val === target) {
return true;
}
}
return false;
}

add(num: number): void {
let curr = this.head;
const level = this.randomLevel();
const node = new Node(num, level);
this.level = Math.max(this.level, level);

for (let i = this.level - 1; i >= 0; i--) {
curr = this.findClosest(curr, i, num);
if (i < level) {
node.next[i] = curr.next[i];
curr.next[i] = node;
}
}
}

erase(num: number): boolean {
let curr = this.head;
let ok = false;

for (let i = this.level - 1; i >= 0; i--) {
curr = this.findClosest(curr, i, num);
if (curr.next[i] && curr.next[i]!.val === num) {
curr.next[i] = curr.next[i]!.next[i];
ok = true;
}
}

while (this.level > 1 && this.head.next[this.level - 1] === null) {
this.level--;
}

return ok;
}

private findClosest(curr: Node, level: number, target: number): Node {
while (curr.next[level] && curr.next[level]!.val < target) {
curr = curr.next[level]!;
}
return curr;
}

private randomLevel(): number {
let level = 1;
while (level < Skiplist.maxLevel && Math.random() < Skiplist.p) {
level++;
}
return level;
}
}

/**
* Your Skiplist object will be instantiated and called as such:
* var obj = new Skiplist()
* var param_1 = obj.search(target)
* obj.add(num)
* var param_3 = obj.erase(num)
*/
```

<!-- tabs:end -->

<!-- solution:end -->
Expand Down
89 changes: 89 additions & 0 deletions solution/1200-1299/1206.Design Skiplist/Solution.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
class Node {
val: number;
next: (Node | null)[];

constructor(val: number, level: number) {
this.val = val;
this.next = Array(level).fill(null);
}
}

class Skiplist {
private static maxLevel: number = 32;
private static p: number = 0.25;
private head: Node;
private level: number;

constructor() {
this.head = new Node(-1, Skiplist.maxLevel);
this.level = 0;
}

search(target: number): boolean {
let curr = this.head;
for (let i = this.level - 1; i >= 0; i--) {
curr = this.findClosest(curr, i, target);
if (curr.next[i] && curr.next[i]!.val === target) {
return true;
}
}
return false;
}

add(num: number): void {
let curr = this.head;
const level = this.randomLevel();
const node = new Node(num, level);
this.level = Math.max(this.level, level);

for (let i = this.level - 1; i >= 0; i--) {
curr = this.findClosest(curr, i, num);
if (i < level) {
node.next[i] = curr.next[i];
curr.next[i] = node;
}
}
}

erase(num: number): boolean {
let curr = this.head;
let ok = false;

for (let i = this.level - 1; i >= 0; i--) {
curr = this.findClosest(curr, i, num);
if (curr.next[i] && curr.next[i]!.val === num) {
curr.next[i] = curr.next[i]!.next[i];
ok = true;
}
}

while (this.level > 1 && this.head.next[this.level - 1] === null) {
this.level--;
}

return ok;
}

private findClosest(curr: Node, level: number, target: number): Node {
while (curr.next[level] && curr.next[level]!.val < target) {
curr = curr.next[level]!;
}
return curr;
}

private randomLevel(): number {
let level = 1;
while (level < Skiplist.maxLevel && Math.random() < Skiplist.p) {
level++;
}
return level;
}
}

/**
* Your Skiplist object will be instantiated and called as such:
* var obj = new Skiplist()
* var param_1 = obj.search(target)
* obj.add(num)
* var param_3 = obj.erase(num)
*/