介绍一下跳表选层和插入过程?

 跳表 (Skip List)‌是一种随机化的数据结构,实质上是一种可以进行二分查找的有序链表。跳表通过增加多级索引,提高了查找、插入和删除操作的效率。

跳表支持平均O(logn)、最坏O(n)复杂度的节点查找,还可以通过顺序性操作来批量处理节点。大部分情况下性能接近平衡树(如红黑树)。

一、跳表的选层机制

跳表的选层机制是通过随机函数来决定的。当需要插入或更新一个节点时,跳表会通过随机函数确定需要建立索引的层数。例如,要插入一个元素时,跳表会随机生成一个层数,然后从链表的底层开始,逐层向上建立索引。这种随机化机制确保了跳表的平衡性,使得各层的元素分布较为均匀‌。

具体规则如下:

  1. 随机函数决定层级

    • 插入新节点时,通过随机算法确定该节点的层级。
    • 常见方法是模拟“抛硬币”实验:每次抛硬币,若为正面则层级加1,直到抛出反面为止。
    • Redis 中的实现
      • 概率 p = 1/4(即每次有25%的概率增加层级)。
      • 最大层级 maxLevel = 32(超过此值不再增加层级)。
    • 结果
      • 节点的层级越高,概率越低。例如,层级为1的概率为 3/4,层级为2的概率为 3/16,层级为3的概率为 3/64,依此类推。
      • 平均每个节点的层级约为 1/p = 4(当 p=1/4 时)。
  2. 层级分配示例

    • 若新节点的层级为 k,则它会在跳表的 Level 1 到 Level k 中各层插入一个指针。
    • 如果 k 大于当前跳表的最大层级,则更新跳表的最大层级为 k

二、跳表的插入过程

插入操作的核心是确定新节点的层级,并更新各层级的指针。以下是具体步骤:

1. 确定新节点的层级
  • 使用随机函数(如抛硬币)生成新节点的层级 k
    • 例如,在 Redis 中,通过 zslRandomLevel() 函数实现,初始层级为1,每次以 p=1/4 的概率递增,直到达到最大层级或抛出反面。
2. 找到插入位置
  • 从跳表的最高层开始,逐层向下查找插入位置:

    1. 在当前层级向右遍历,找到小于目标值的最大节点
    2. 如果当前层级的下一个节点值大于目标值,或到达层级末尾,则下降到下一层。
    3. 重复上述步骤,直到到达最底层(Level 0),此时确定插入位置。
  • 记录路径
    在查找过程中,维护一个 update 数组,记录每一层级的前驱节点(用于后续更新指针)。

3. 逐层插入新节点
  • 在每一层级(从 Level 1 到 Level k)中:
    1. 将新节点的 next 指针指向 update[i] 的下一个节点。
    2. 将 update[i] 的 next 指针指向新节点。
  • 如果新节点的层级 k 超过跳表当前最大层级,则更新跳表的最大层级为 k
4. 更新相关属性(索引)
  • 节点计数:跳表的总节点数加1。
  • 跨度(span)(可选):某些实现中会维护节点间的跨度(即到下一节点的距离),插入后需更新受影响节点的跨度。

三、查找与删除

1. 查找(Search)

查找操作的目标是在跳表中找到一个特定值的位置。从最高层开始,向右移动直到找到一个大于目标值的节点,然后下降到下一层继续这个过程,直到到达最底层或者找到了目标值。

  • 步骤:
    • 从最高层的头节点开始。
    • 向右移动,直到遇到一个比目标值大的节点或到达链表尾部。
    • 下降到下一层,重复上述步骤。
    • 如果在最底层找到了目标值,则返回该节点;否则,表示跳表中不存在此值。

2. 删除(Deletion)

删除操作包括找到待删除的节点并从所有涉及的层中移除它,同时调整相关节点的指针。

  • 步骤:
    • 首先执行查找操作定位到要删除的节点。
    • 对于每个包含该节点的层,调整前驱节点的指针,使其跳过该节点。
    • 释放被删除节点占用的资源。
    • 如果删除的是某一层的唯一节点,可能需要减少跳表的层数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值