菜鸡一只,这次来说另一个数据结构,跳表SkipList!
一、引言
在排好顺序的数组中,要查找某个元素,我们可以通过二分法来快速查找,这其实依赖的是数组的随机访问的特性,我可以通过arr[6],来访问数组中的第七个元素(因为第一个元素下标是0),因此我就可以跳跃着访问数组中任意一个元素。
但如果是普通的链表,第七个元素的地址,我只能通过第六个元素知道,第六个元素的地址我只能通过第五个元素知道,以此类推,我如果要查找链表中的任意一个元素,我都需要从头遍历,就使用不了二分法查找,那么对于链表这样的数据结构的快速查询,是否有合理的解决方案?有的,把链表变为跳表!
二、数据结构
以下这些制作精美的图片
来自于王争老师的《算法与数据结构之美》,我只是搬运工!!!
如果我们在原始链表之上能够加一层索引,查询数据的时候,从索引开始查,那么就能够少遍历一些节点
甚至,我可以再加一层索引
假设我们要查找64个数字中的第62个数
如图所示,原来需要遍历62次,现在只需要遍历11个结点,那在跳表中查询一个数据的时间复杂度就是O(m*logn),m=3(这是公式算出来的,我这里就不具体说了)
三、增删
java中跳表有两个支持高并发的类:ConcurrentSkipListSet和ConcurrentSkipListMap
有一位大神,对源码进行了详细的剖析注释,大家如果有想认真研究,请看下面的链接
死磕 java集合之ConcurrentSkipListMap源码分析——发现个bug(作者:彤哥读源码):
1、插入数据
总共有三个步骤:
(1)、找到需要插入的位置(这一步就和查找是一样的)
(2)、判断当前节点应该在哪一层开始创建索引,创建索引建立上下节点的连接关系
(3)、建好索引后,将新的索引和以前的索引连接起来
具体代码可以参考ConcurrentSkipListMap的doPut
2、删除数据
删除的话,要考虑这么几个问题:
(1)、删除操作做到一半,是否有其他线程也在做这个操作,同时做的话是否会报错
(2)、不单单是最底下的数据要删除,如果这个数据有在索引中,那么上面的索引也都要删除
(2)、假设删除的数据在最上层索引,并且最上层索引只有他一个值,那么整个跳表结构层级要减一
具体代码可以参考ConcurrentSkipListMap的doRemove
四、好奇
有耐心的还是建议认真看看上面那位大神的源码解析,没耐心的看我下面的解释就好了
1、插入数据的时候创建索引的比例是多少?
(rnd & 0x80000001) == 0
条件为True的概率是多少?
是否创建索引,首先概率肯定不是百分之50(因为代码中创建一个随机值,然后判断随机值是否是正偶数,然而随机值有可能是负奇数,负偶数,正奇数,正偶数),所以大概是百分之25(我还测试了下验证了25%)
运行10W次,结果都在2W5左右
2、计算每个数据出现在的层级的概率是多少
while (((rnd >>>= 1) & 1) != 0)
++level;
计算得到每个level的大概比例是多少?
可以看到,层级为1的概率基本是50%,层级2的概率是层级1的百分之50,以此类推,(这其实是一个概率题)
五、总结
1、时间复杂度
跳表是一种动态数据结构,支持快速的插入、删除、查找操作,时间复杂度都是O(logn)。
2、空间复杂度
跳表的空间复杂度是O(n),也就是说,如果将包含n个结点的单链表构造成跳表,我们需要额外再用接近n个结点的存储空间
但是这就引申出另一方面的思考,如果跳表中本身存的单个数据很大,那么多出的这些空间复杂度,其实只是存了地址(并不实际存储数据),这样看来,其实也不是在任何场景查下都会消耗双倍的资源
3、与红黑树的区别
红黑树的插入、删除、查找操作,时间复杂度也都是O(logn),那么他们之间有什么区别呢?
问:为什么Redis用跳表来实现有序集合,而不用红黑树?
王争老师给出的答案大致如下:
Redis中的有序集合支持的核心操作主要有下面这几个:
(1)、插入一个数据;
(2)、删除一个数据;
(3)、查找一个数据;
(4)、迭代输出有序序列
(5)、按照区间查找数据(比如查找值在[100, 356]之间的数据)
其实问题就出在第(5)条,红黑树不太方便做区间值查找(当然要做肯定是可用的,中序遍历就行,但是数据个数相同的情况下,中序遍历在遍历数据时走过的路线肯定是比跳表长的)
所以为什么数据库在做索引的时候用的是b+树(在最下层的叶子节点上,会通过链表的方式,把所有叶子串起来),目的也是一样的,在遇到区间查找的情况下,能通过查找到的开头的叶子节点,直接往后遍历得到区间中所有的数据(这样就不用走中序遍历绕路了)!
菜鸡一只,如果有哪里说错的地方,还请大家批评指出(一定坚决改正,做到不误导新人)!
最近也是好久没有写文章了,各种在路上,在去见客户的路上,在往返上海杭州的路上,不过学习还是该学习的,努力还是该努力的,一定要输出的同时也保证输入啊,肚子里没点墨水很快就会被淘汰了!!其实最近真的很想赶快把这些基础的数据结构更新好的,但是苦于时间有限,不光是工作的上的事情,自己想搞的事情也是比较多的,又要踢球运动减肥,又要看看漫画刷刷剧,而且十一也快来了,蠢蠢欲动的回家的心~好啦下次再见拜拜~