Open azl397985856 opened 1 year ago
从上往下查询 从下往上删除
class Node:
def __init__(self,val=0,right=None,down=None):
self.val=val
self.right=right
self.down=down
class Skiplist:
def __init__(self):
left=[Node(-1) for _ in range(16)]
right=[Node(20001) for _ in range(16)]
for i in range(15):
left[i].right=right[i]
left[i].down=left[i+1]
right[i].down=right[i+1]
left[-1].right=right[-1]
self.root=left[0]
def search(self, target: int) -> bool:
cur=self.root
while cur:
if target<cur.right.val:
cur=cur.down
elif target>cur.right.val:
cur=cur.right
else:
return True
return False
def add(self, num: int) -> None:
cur=self.root
stack=[]
while cur:
if cur.right.val>=num:
stack.append(cur)
cur=cur.down
else:
cur=cur.right
pre=None
while stack:
cur=stack.pop()
node=Node(num)
node.right=cur.right
cur.right=node
if pre:
node.down=pre
pre=node
if random.randint(0,1):
break
def erase(self, num: int) -> bool:
cur=self.root
removed=False
while cur:
if num<cur.right.val:
cur=cur.down
elif num>cur.right.val:
cur=cur.right
else:
cur.right=cur.right.right
removed=True
cur=cur.down
return removed
复杂度分析
# 1 先创建一个跳表节点Node,需要包含以下三个参数
# 节点值val
# 右边节点right
# 下边节点down
# 2 跳表初始化
# 每一层设置左右两个哨兵节点,左节点的值要比所有值小,右节点的值要比所有值大。因为0 <= num, target <= 20000,因此左节点值设为 -1,右节点值设为20001即可。
# 跳表的最大层数为16。因为题目说了最多调用 50000 次 search, add, 以及 erase操作,也就是底层链表长度n最长为50000,而我们随机的选 n/2 个元素做为一级索引,随机选 n/4 个元素作为二级索引,……,随机选n/2^k 作为顶层索引。而 2^16=65536>50000 所以16层足够为长度50000的链表建立索引。
# 每层的两个哨兵节点中,左节点要指向右节点,并且除了最后一层外左右节点都需要指向下一层节点
# 跳表起始节点head即为left[0],即顶层的左哨兵节点
# 3 查找(search)方法。
#从head节点开始查找,如果当前节点cur的右边节点值比target大,应该往下查找;如果cur的右边节点值比target小,应该往右查找;如果相等,那就是找到了,返回True。遍历完了也没找到,返回False。
# 4 插入(add)方法。
# 相对比较复杂,因为还需要根据概率维护索引。
# 查找插入位置。也是从head开始查找插入的位置,每次往下走的节点都需要保存,因为可能在该节点后面插入新的节点作为索引。换句话说,其实查找的是在每一层应该插入的位置并记录在stack中,只是不一定每一层都会插入节点罢了。
# 插入节点。首先最后一层肯定是要插入节点的,插入完成后,记录一下该层节点,因为如果该节点要加入到索引中,上一层是要指向这一层的。以1/2的概率生成索引,在上一层插入节点;1/2的概率插入结束,不再继续维护索引;如果前面生成了索引,再以1/2的概率插入节点直到插入到顶层或中途因概率停止插入。
# 5 删除(erase)方法。
# 删除和查找差不多,只不过是对每一层找到的节点都要进行删除,也就是删除节点的同时要删除由其产生的索引节点。
class Node:
def __init__(self, val = 0):
self.val = val
self.right = None
self.down = None
class Skiplist:
def __init__(self):
# 每层设置两个哨兵节点,左节点的值要比所有值小,右节点的值要比所有值大。16是跳表的最大层数
left = [Node(-1) for _ in range(16)]
right = [Node(20001) for _ in range(16)]
for i in range(15):
left[i].right = right[i]
left[i].down = left[i + 1]
right[i].down = right[i + 1]
left[-1].right = right[-1]
self.head = left[0]
def search(self, target: int) -> bool:
cur = self.head
while cur:
if cur.right.val > target:
cur = cur.down
elif cur.right.val < target:
cur = cur.right
else:
return True
return False
def add(self, num: int) -> None:
cur = self.head
stack = []
while cur:
if cur.right.val >= num:
stack.append(cur)
cur = cur.down
else:
cur = cur.right
pre = None
while stack:
cur = stack.pop()
node = Node(num)
node.right = cur.right
cur.right = node
if pre:
node.down = pre
pre = node
if random.randint(0, 1):
break
def erase(self, num: int) -> bool:
cur = self.head
is_removed = False
while cur:
if cur.right.val >= num:
if cur.right.val == num:
is_removed = True
cur.right = cur.right.right
cur = cur.down
else:
cur = cur.right
return is_removed
import random
class Node:
def __init__(self, val):
self.val = val
self.next = None
self.down = None
class Skiplist:
def __init__(self):
self.head = Node(-1)
self.levels = [self.head]
def search(self, target):
curr = self.head
while curr is not None:
if curr.val == target:
return True
elif curr.next is None or curr.next.val > target:
curr = curr.down
else:
curr = curr.next
return False
def add(self, num):
path = self._get_path()
node = Node(num)
prev = None
while path:
curr = path.pop()
node.next = curr.next
curr.next = node
node.down = prev
prev = node
if random.random() < 0.5:
new_level = Node(-1)
new_level.next = self.levels[-1]
self.levels.append(new_level)
new_level.down = prev
prev = new_level
def erase(self, num):
found = False
curr = self.head
while curr is not None:
if curr.val == num:
curr.next = curr.next.next
found = True
elif curr.next is None or curr.next.val > num:
curr = curr.down
else:
curr = curr.next
return found
def _get_path(self):
path = []
curr = self.head
while curr is not None:
if curr.next is None:
path.append(curr)
curr = curr.down
elif curr.next.val == float('inf'):
break
elif curr.next.val > float('inf'):
curr = curr.down
else:
curr = curr.next
return path
class Skiplist {
static final int MAX_LEVEL = 32;
static final double P_FACTOR = 0.25;
private SkiplistNode head;
private int level;
private Random random;
public Skiplist() {
this.head = new SkiplistNode(-1, MAX_LEVEL);
this.level = 0;
this.random = new Random();
}
public boolean search(int target) {
SkiplistNode curr = this.head;
for (int i = level - 1; i >= 0; i--) {
/* 找到第 i 层小于且最接近 target 的元素*/
while (curr.forward[i] != null && curr.forward[i].val < target) {
curr = curr.forward[i];
}
}
curr = curr.forward[0];
/* 检测当前元素的值是否等于 target */
if (curr != null && curr.val == target) {
return true;
}
return false;
}
public void add(int num) {
SkiplistNode[] update = new SkiplistNode[MAX_LEVEL];
Arrays.fill(update, head);
SkiplistNode curr = this.head;
for (int i = level - 1; i >= 0; i--) {
/* 找到第 i 层小于且最接近 num 的元素*/
while (curr.forward[i] != null && curr.forward[i].val < num) {
curr = curr.forward[i];
}
update[i] = curr;
}
int lv = randomLevel();
level = Math.max(level, lv);
SkiplistNode newNode = new SkiplistNode(num, lv);
for (int i = 0; i < lv; i++) {
/* 对第 i 层的状态进行更新,将当前元素的 forward 指向新的节点 */
newNode.forward[i] = update[i].forward[i];
update[i].forward[i] = newNode;
}
}
public boolean erase(int num) {
SkiplistNode[] update = new SkiplistNode[MAX_LEVEL];
SkiplistNode curr = this.head;
for (int i = level - 1; i >= 0; i--) {
/* 找到第 i 层小于且最接近 num 的元素*/
while (curr.forward[i] != null && curr.forward[i].val < num) {
curr = curr.forward[i];
}
update[i] = curr;
}
curr = curr.forward[0];
/* 如果值不存在则返回 false */
if (curr == null || curr.val != num) {
return false;
}
for (int i = 0; i < level; i++) {
if (update[i].forward[i] != curr) {
break;
}
/* 对第 i 层的状态进行更新,将 forward 指向被删除节点的下一跳 */
update[i].forward[i] = curr.forward[i];
}
/* 更新当前的 level */
while (level > 1 && head.forward[level - 1] == null) {
level--;
}
return true;
}
private int randomLevel() {
int lv = 1;
/* 随机生成 lv */
while (random.nextDouble() < P_FACTOR && lv < MAX_LEVEL) {
lv++;
}
return lv;
}
}
class SkiplistNode {
int val;
SkiplistNode[] forward;
public SkiplistNode(int val, int maxLevel) {
this.val = val;
this.forward = new SkiplistNode[maxLevel];
}
}
/**
* Your Skiplist object will be instantiated and called as such:
* Skiplist obj = new Skiplist();
* boolean param_1 = obj.search(target);
* obj.add(num);
* boolean param_3 = obj.erase(num);
*/
1206. 设计跳表
入选理由
暂无
题目地址
https://leetcode-cn.com/problems/design-skiplist/
前置知识
暂无
题目描述
不使用任何库函数,设计一个跳表。
跳表是在 O(log(n)) 时间内完成增加、删除、搜索操作的数据结构。跳表相比于树堆与红黑树,其功能与性能相当,并且跳表的代码长度相较下更短,其设计思想与链表相似。
例如,一个跳表包含 [30, 40, 50, 60, 70, 90],然后增加 80、45 到跳表中,以下图的方式操作:
Artyom Kalinin [CC BY-SA 3.0], via Wikimedia Commons
跳表中有很多层,每一层是一个短的链表。在第一层的作用下,增加、删除和搜索操作的时间复杂度不超过 O(n)。跳表的每一个操作的平均时间复杂度是 O(log(n)),空间复杂度是 O(n)。
在本题中,你的设计应该要包含这些函数:
bool search(int target) : 返回 target 是否存在于跳表中。 void add(int num): 插入一个元素到跳表。 bool erase(int num): 在跳表中删除一个值,如果 num 不存在,直接返回 false. 如果存在多个 num ,删除其中任意一个即可。 了解更多 : https://en.wikipedia.org/wiki/Skip_list
注意,跳表中可能存在多个相同的值,你的代码需要处理这种情况。
样例: