题目描述:
不使用任何库函数,设计一个 跳表 。
跳表 是在 O(log(n))
时间内完成增加、删除、搜索操作的数据结构。跳表相比于树堆与红黑树,其功能与性能相当,并且跳表的代码长度相较下更短,其设计思想与链表相似。
例如,一个跳表包含 [30, 40, 50, 60, 70, 90]
,然后增加 80
、45
到跳表中,以下图的方式操作:

跳表中有很多层,每一层是一个短的链表。在第一层的作用下,增加、删除和搜索操作的时间复杂度不超过 O(n)
。跳表的每一个操作的平均时间复杂度是 O(log(n))
,空间复杂度是 O(n)
。
了解更多 : 跳表 - OI Wiki
在本题中,你的设计应该要包含这些函数:
bool search(int target)
: 返回target是否存在于跳表中。void add(int num)
: 插入一个元素到跳表。bool erase(int num)
: 在跳表中删除一个值,如果num
不存在,直接返回false. 如果存在多个num
,删除其中任意一个即可。
注意,跳表中可能存在多个相同的值,你的代码需要处理这种情况。
代码思路:
跳表是一种随机化的数据结构,允许快速查找、插入和删除操作。它通过多层链表实现,每一层都是前一层的子集,且最高层的链表最短。查找、插入和删除操作从最顶层开始,逐层向下进行,直到找到或操作完成于最底层。
跳表类(Skiplist)主要方法思路:
- 初始化(
__init__
) :- 创建一个头节点(值为负无穷大),一个尾节点(值为正无穷大),并将头节点的
next
列表的第一个元素指向尾节点。 - 初始化跳表的最高层数为0。
- 创建一个头节点(值为负无穷大),一个尾节点(值为正无穷大),并将头节点的
- 查找(
search
) :- 从最高层开始,逐层向下查找目标值。
- 在每一层中,通过比较目标值和当前节点的下一个节点的值来移动指针。
- 如果找到目标值,返回
True
;如果遍历完所有层仍未找到,返回False
。
- 添加(
add
) :- 从最高层开始,逐层向下找到插入位置,并记录路径。
- 在最底层(第0层)插入新节点。
- 使用概率为1/2的随机抛硬币决定是否将新节点提升到更高层。
- 如果提升到比当前最高层还高的层,需要更新头节点的
next
列表和新节点的next
列表,并增加跳表的最高层数。
- 删除(
erase
) :- 从最高层开始,逐层向下查找目标值。
- 在每一层中,如果找到目标值,将其从链表中删除,并标记操作成功。
- 返回操作是否成功的标志。
代码实现:
import random
class Node:
def __init__(self, val):
self.val = val # 节点值
self.next = [] # 指向下一节点的列表,不同层对应不同节点
class Skiplist:
def __init__(self):
self.head = Node(-float('inf')) # 头节点,值为负无穷大
self.tail = Node(float('inf')) # 尾节点,值为正无穷大
self.head.next.append(self.tail) # 头节点指向尾节点
self.levels = 0 # 最高层数
def search(self, target: int) -> bool:
# 类似于add方法
level = self.levels # 从最高层开始
cur = self.head # 当前节点从头节点开始
while level != -1:
while target > cur.next[level].val:
cur = cur.next[level] # 移动到下一个节点
if target == cur.next[level].val:
return True # 找到目标值
level -= 1 # 移动到下一层
return False # 未找到目标值
def add(self, num: int) -> None:
level = self.levels # 从最高层开始
path = [] # 记录路径,用于后续提升
cur = self.head # 当前节点从头节点开始
while level != -1:
while num >= cur.next[level].val:
cur = cur.next[level] # 移动到下一个节点
path.append(cur) # 记录当前路径节点
level -= 1 # 移动到下一层
node = Node(num) # 创建新节点
# 在第0层插入新节点
node.next.append(path[-1].next[0])
path[-1].next[0] = node
# 尝试提升新节点
def coin_flip():
return random.random() > 0.5 # 抛硬币,返回True或False
level = 1 # 从第1层开始尝试提升
while coin_flip():
if level > self.levels:
self.head.next.append(node) # 更新头节点指向新节点
node.next.append(self.tail) # 新节点指向尾节点
self.levels += 1 # 增加最高层数
else:
i = len(path) - 1 - level # 计算路径中对应层的节点索引
node.next.append(path[i].next[level])
path[i].next[level] = node
level += 1 # 尝试提升到下一层
def erase(self, num: int) -> bool:
level = self.levels # 从最高层开始
cur = self.head # 当前节点从头节点开始
flag = False # 操作成功标志
while level != -1:
while num > cur.next[level].val:
cur = cur.next[level] # 移动到下一个节点
if cur.next[level].val == num:
flag = True # 找到目标值,标记操作成功
cur.next[level] = cur.next[level].next[level] # 从链表中删除目标节点
level -= 1 # 移动到下一层
return flag # 返回操作成功标志