欢迎光临
我们一直在努力

数据结构基础:跳表全面解析

什么是跳表

跳表(Skip List)是一种随机化的数据结构,基于并联的有序链表,其效率可比拟于二叉搜索树(如红黑树、AVL树等)。跳表的平均查找和插入时间复杂度都是O(log n),但与平衡树相比,跳表的实现更为简单,且常数因子更小。

跳表是对有序链表的一种扩展,通过维护一系列分层的链表,并在每一层中跳过部分元素,从而加快查找速度。跳表上层的链表作为下层链表的\”快速通道\”,使得在查找时可以先在高层链表中进行大步跳跃,再在底层链表中进行精确定位。

为什么需要跳表

传统的有序链表虽然插入和删除操作比较方便(只需要修改指针),但查找操作需要从头开始遍历,时间复杂度为 O(n)。如果链表很长,查找效率会非常低。

平衡树(如 AVL 树、红黑树)可以实现 O(logn) 的查找、插入和删除,但它们的实现比较复杂,需要进行旋转等操作来维护树的平衡。

跳表提供了一种折衷方案:它在实现复杂度上接近链表,但在性能上接近平衡树。

基本原理

基本结构

跳表由多层链表组成,每一层都是一个有序链表,底层包含所有元素,而上层则是下层的子集。具体来说:

  • 最底层(Level 0)是一个普通的有序链表,包含所有元素
  • 第一层(Level 1)大约包含每两个元素中的一个
  • 第二层(Level 2)大约包含每四个元素中的一个
  • 依此类推,第i层大约包含每2^i个元素中的一个

节点结构

每个跳表节点包含:

  • 值(key):节点存储的实际数据
  • 多个前向指针:指向同层的下一个节点
  • 层数(level):决定节点在哪些层出现

class SkipListNode:
def __init__(self, key, level):
self.key = key
# 创建前向指针数组,长度为level+1
self.forward = [None] * (level + 1)

class SkipList:
def __init__(self, max_level, p):
# 最大层数
self.MAX_LEVEL = max_level
# 用于随机层数的概率参数
self.P = p
# 当前跳表的层数
self.level = 0
# 头节点,key为-1
self.header = SkipListNode(1, self.MAX_LEVEL)

跳表核心操作

随机层数生成

跳跳表使用类似于抛硬币的方式来决定一个新节点的层数:

  • 新节点默认在最底层(Level 0)出现
  • 使用概率参数p(通常为0.5或0.25)决定是否上升到更高层
  • 生成一个随机数r在[0,1]范围内
  • 如果r < p,则层数加1,节点会出现在Level 1
  • 继续生成随机数和比较,直到某次r >= p或达到最大层数
  • 这种随机层数生成机制的特点是:

    • 每个节点至少在Level 0层出现
    • 每一层上的节点数量大约是下一层的一半(当p=0.5时)
    • 最底层包含所有节点,向上每层节点数量递减
    • 平均来说,大约有1/2的节点会出现在Level 1
    • 大约有1/4的节点会出现在Level 2
    • 大约有1/8的节点会出现在Level 3
    • 以此类推…

    def random_level(self):
    \”\”\”生成随机层数\”\”\”
    level = 0
    # 抛硬币决定是否上升层级
    while random.random() < self.P and level < self.MAX_LEVEL:
    level += 1
    return level

    搜索操作

    跳表的搜索从最高层开始,然后逐层向下:

  • 从头节点的最高层开始
  • 如果当前节点的前向指针指向的节点值小于目标值,则向前移动
  • 否则,降低一层继续搜索
  • 重复上述过程,直到达到最底层并找到目标值或确定其不存在
  • def search(self, key):
    \”\”\”查找指定的key\”\”\”
    current = self.header

    # 从最高层开始,逐层向下查找
    for i in range(self.level, 1, 1):
    # 在当前层水平移动,直到找到小于或等于目标值的最大节点
    while current.forward[i] and current.forward[i].key < key:
    current = current.forward[i]

    # 现在在底层,检查下一个节点是否是目标值
    current = current.forward[0]

    # 如果下一个节点存在且key相等,则找到目标
    if current and current.key == key:
    return current

    # 没有找到目标
    return None

    示例

    查找值为12的节点的过程。搜索从头节点的最高层开始,逐层向下进行。

    步骤一**从最高层开始搜索**:

    • 搜索从跳表的最高层(Level 2)的头节点开始。我们要查找值为12的节点。
    • 当前位置:Level 2的Head节点
    • 目标:找到值为12的节点

    步骤二**在最高层水平移动到合适位置**:

    • 在Level 2层,Head的next指向值为7的节点。因为7 < 12,所以我们移动到值为7的节点。
    • 当前位置:Level 2的节点7
    • 判断:7 < 12,可以继续向右移动

    步骤三**当前层无法继续前进,降低层级**:

    • 在Level 2层,节点7的next指向值为18的节点。因为18 > 12,所以我们无法继续向右移动。此时需要降到Level 1层继续搜索。
    • 当前位置:Level 1的节点7
    • 判断:在Level 2中,下一个节点值18 > 12,无法继续水平移动,需要降级

    步骤四**在中间层继续搜索**:

    • 在Level 1层,节点7的next指向值为12的节点。因为12 = 12,我们找到了目标值,但仍然需要降到最底层以确认节点是否存在于最底层。
    • 当前位置:Level 1的节点12
    • 判断:12 = 12,找到目标值,继续降级到最底层确认

    步骤五**在最底层确认结果**:

    • 在Level 0(最底层),我们确认值为12的节点确实存在。搜索成功完成。
    • 当前位置:Level 0的节点12
    • 结果:成功找到目标节点12

    搜索路径总结:

  • 从Level 2的Head节点开始
  • 移动到Level 2的节点7
  • 发现下一个节点18 > 12,降到Level 1
  • 在Level 1找到节点12,等于目标值
  • 降到Level 0确认节点12存在
  • 搜索成功完成
  • 搜索操作的时间复杂度为O(log n),因为我们利用了跳表的分层结构,每一层大约跳过了一半的节点。

    插入操作

    跳表的插入过程包括:

  • 搜索合适的插入位置,同时记录每一层需要更新的节点
  • 为新节点随机生成一个层数
  • 如果新节点的层数高于当前跳表的层数,更新跳表层数
  • 更新所有受影响层的前向指针
  • def insert(self, key):
    \”\”\”插入指定的key\”\”\”
    # 创建更新数组,用于存储需要更新前向指针的节点
    update = [None] * (self.MAX_LEVEL + 1)
    current = self.header

    # 从最高层开始,查找合适的插入位置
    for i in range(self.level, 1, 1):
    while current.forward[i] and current.forward[i].key < key:
    current = current.forward[i]
    # 记录每一层需要更新的节点
    update[i] = current

    # 移动到下一个节点
    current = current.forward[0]

    # 检查key是否已存在
    if current and current.key == key:
    return False # 已存在,插入失败

    # 为新节点生成随机层数
    random_level = self.random_level()
    if random_level > self.level:
    # 更新跳表当前最大层数
    for i in range(self.level + 1, random_level + 1):
    update[i] = self.header
    self.level = random_level

    # 创建新节点
    new_node = SkipListNode(key, random_level)

    赞(0)
    未经允许不得转载:171主机测评 » 数据结构基础:跳表全面解析
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址