跳表的实现原理

跳表的结构

 

对应在redis中zset 底层结构

那么如何来构建跳表?

构建原理

当每次有数据要插入时,先通过概率算法告诉我们这个元素需要插入到几级索引中,然后开始维护索引并把数据插入到原始链表中。下面开始讲解这个概率算法代码如何实现。 

我们可以实现一个 randomLevel() 方法,该方法会随机生成 1~MAX_LEVEL 之间的数(MAX_LEVEL表示索引的最高层数),且该方法有 1/2 的概率返回 1、1/4 的概率返回 2、1/8的概率返回 3,以此类推。 

  • randomLevel() 方法返回 1 表示当前插入的该元素不需要建索引,只需要存储数据到原始链表即可(概率 1/2)

  • randomLevel() 方法返回 2 表示当前插入的该元素需要建一级索引(概率 1/4)

  • randomLevel() 方法返回 3 表示当前插入的该元素需要建二级索引(概率 1/8)

  • randomLevel() 方法返回 4 表示当前插入的该元素需要建三级索引(概率 1/16)

  • randomLevel() 方法返回 4 表示当前插入的该元素需要建四级索引(概率 1/32)

  • ... 以此类推

该 randomLevel 方法会随机生成 1~MAX_LEVEL 之间的数,且 : 

  • 1/2 的概率返回 1  

  • 1/4 的概率返回 2  

  • 1/8 的概率返回 3  

  • ... 以此类推 

晋升概率 SKIPLIST_P 设置为 1/2, 代表每两个节点抽出一个节点作为上一级索引, 如果想节省空间可以适当降低该值,redis中的值为: 0.25 = 1/4,每四个节点抽出一个节点作为上一级索引。

private int randomLevel() {  

     int level = 1;  

    // 当 level < MAX_LEVEL,且随机数小于设定的晋升概率时,level + 1 

    while (Math.random() < SKIPLIST_P && level < MAX_LEVEL) {

       level += 1; 
    }

    return level;  

} 

注意: 因为 randomLevel() 方法返回值 > 1就会建索引,凡是建索引,无论几级索引必然有一级索引,所以一级索引中元素个数占原始数据个数的比率为 randomLevel() 方法返回值 > 1 的概率。那 randomLevel() 方法返回值 > 1 的概率是多少呢?

因为 randomLevel() 方法随机生成 1~MAX_LEVEL 的数字,且 randomLevel() 方法返回值 1 的概率为 1/2,则 randomLevel() 方法返回值 > 1 的概率为 1 - 1/2 = 1/2。即通过上述流程实现了一级索引中元素个数占原始数据个数的 1/2。 这样就可以满足我们想要的结果,

即:一级索引中元素个数应该占原始数据的 1/2,二级索引中元素个数占原始数据的 1/4,三级索引中元素个数占原始数据的 1/8 ,依次类推,一直到最顶层索引。 

时间复杂度

跳表插入、删除、查找元素的时间复杂度跟红黑树都是一样量级的,时间复杂度都是O(logn)

空间复杂度

假如原始链表包含 n 个元素,则一级索引元素个数为 n/2、二级索引元素个数为 n/4、三级索引元素个数为 n/8 以此类推。所以,索引节点的总和是:n/2 + n/4 + n/8 + … + 8 + 4 + 2 = n-2,空间复杂度是 O(n)。

如果每三个结点抽一个结点做为索引,索引总和数就是 n/3 + n/9 + n/27 + … + 9 + 3 + 1= n/2,减少了一半。

所以, 我们可以通过较少索引数来减少空间复杂度,但是相应的肯定会造成查找效率有一定下降,我们可以根据我们的应用场景来控制这个阈值。 

为什么Redis选择使用跳表而不是红黑树来实现有序集合? 

zset有个查找需求特性:支持按照score范围区间查找元素

跳表:可以做到 O(logn) 的时间复杂度定位区间的起点,然后在原始链表中顺序往后遍历就可以了,非常高效,但是红黑树不行。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JYCJ_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值