Skip Lists: A Probabilistic Alternative to Balanced Trees 阅读笔记
论文地址:https://15721.courses.cs.cmu.edu/spring2018/papers/08-oltpindexes1/pugh-skiplists-cacm1990.pdf
关键点:
1、在算法内部引入随机性,从而避免对插入顺序随机性的依赖
综述
跳表使用一种概率上的平衡而非强制平衡(相较于balance tree),使得插入和删除操作比同类的平衡算法(balance tree)更低。
前言
binary tree可用于表示抽象的数据结构,例如字典、有序列表等。当元素为按随机顺序插入时,binary tree可以很好的工作,但当元素按顺序插入时,性能急剧退化。如果在插入前,可以对插入的元素进行随机化排序,那么就可以使得binary tree拥有良好的性能。但是由于binary tree通常需要在线响应动作(In most cases queries must be answered on-line),因此提前将待插入元素随机化是不切实际的。balance tree算法会在执行树的操作时,re-arange树的形状,使得树始终保持某种平衡规则(例如左子树与右子树高度之差不超过1),以保持树的检索性能。
跳表是平衡树的一种概率替代方案。
跳表通过咨询(consulting??)一个随机数生成器来实现平衡。(Skip lists have balance properties similar to that of search trees built by random insertions, yet do not require insertions to be random.)
(既然不能保证insert的顺序是随机的,那么就在算法内部引入一个随机来达到保持平衡的效果)
SKIP LISTS
当搜索一个linked list时,我们需要遍历这个linked list中的每个元素。时间复杂度O(N)。
若链表是有序的,并且每个node上都保存了到达下两跳节点的指针(has a pointer to the node two ahead it in the list),那么我们最多只需要搜索个节点。
同理,若我们保存了下四跳的节点,那么需要搜索的节点数就减少至个。
若每第个节点,都有指向第
跳节点的指针,那么在linked list中查找元素时需要检索的节点数量将减少至
个,而指针的数量只翻了一倍。这样的数据结结构将拥有高效的搜索速度,但是对这样的数据结构进行插入或删除操作几乎是不切实际的(因为每插入或删除一个元素,都需要重新调整下x跳指针指向的node)。一个节点若包含若指向下K个节点的指针,则我们称它为level K node。如果每第
个节点都包含后续第
个节点的指针,则每类levels节点的比率分布是固定的:
50%的节点包含level 1,25%的节点包含level 2,12.5%的节点包含level 3,以此类推。
若每个节点的level是随机生成的(但每层的节点概率仍然按照上述比例50%、25%、12.5%...进行分配),会发生什么事情呢?
SKIP LIST ALGORITHMS
这一章节会介绍算法的搜索、插入和删除操作。
搜索:返回某个key对应的value,若key不存在则返回fail。
插入:将制定的key与new value对应起来(若key尚不存在,则新增一个node)。
删除:删除制定的key。(疑问:删除一个node时,如何更新指向这个node的前置节点的level pointer???)
诸如“查找最小key”、“查找下一个key”等额外操作都很容易实现。
跳表中的每一个元素,都用一个node来表示。每个node的层数都是随机生成的,不依赖于当前结构中包含的元素个数。(Each element is represented by a node, the level of which is chosen randomly when the node is inserted without regard for the number of elements in the data structure. )
初始化(Initialization)
初始化一个list,下一跳为nil,当前最大level为1。
搜索算法(Search Algorithm)
按当前节点的leve高pointer向level1 pointer搜索,若forward[i].key < searchKey,则换forward[I]的node继续尝试,直到找到(return value)或找不到(fail)时返回。
插入和删除算法(Insertion and Deletion Algorithms)
在执行插入和删除操作时,算法只是执行简单的搜索和拼接动作。例如:
插入算法
(1)从skip list的header开始,先搜索到比searchKey小的最靠右的node,并存储在update[i]中(i表示层数)
如上图实例中,在完成search时,update中的值为:
update[LEVLE4] = node6
update[LEVEL3] = node6
update[LEVEL2] = node9
update[LEVEL1] = node12
(2)判断node->forward[1]的key是否等于searchKey,若等于则说明key已经存在,则直接更新value=newValue后即可返回;否则,进入(3)
(3)新增一个node,过程如下:
(a)随机生成一个level lvl(但概率还是按照上文提到的比率),若新lvl比现有的最大level高,则执行(a.1)(a.2)
(a.1)将update[currLevel]到update[lvl]数组元素的值都更新为list->header
(a.2)更新list->level为这个新生成的lvl。
(b)new一个新的node X,入参为lvl、searchKey、value
(c)更新node X各level的指针
(c.1) X->forward[i] = update[i]->forward[i]
此时,按上述例子,各个节点的forward信息更新为(因为node17随机生成到的level是2,所以只会有level2和level1的forward指针)
node17->forward[LEVEL2] = update[LEVEL2]->forward[LEVEL2] = node9->forward[LEVEL2] = node25
node17->forward[LEVEL1] = update[LEVEL1]->forward[LEVEL1] = node12->forward[LEVEL1] = node19
(c.2) update[I]->forward[i] = X
此时,按上述例子
update[LEVEL2] = node9, node9->forward[LEVEL2] = node17
update[LEVEL1] = node12, node12->forward[LEVEL1] = node17
插入算法伪代码
删除算法
(1)如同上述插入算法,也是先检索到searchKey所在的NodeX,并且在检索过程中,生成一个udpate[i]向量
(2)对于level 1 -》K (K为当前跳表中最大的有效层数)
(a) 若update[level i]->forward[level i] == nodeX, 则update[level i]->forward[level i] = x->forward[level i];
(b) 若update[level i]->forward[level i] != nodeX, 则 break循环。(因为update[level i]->forward[level i]不为nodeX,说明NodeX的level层数不够高,update[level i]->forward[level i]指向了nodeX之后的其他节点,因此可以break了)
(3)free nodeX
(4)遍历list->level到1,若list->header->forward[level i] == NIL了,则将list->level减一
例如,想要删除node17,那么:
(1)完成search操作后的update向量为
update[4] = node6
update[3] = node6
update[2] = node9
update[1] = node12
(2) 遍历更新前向节点的下k跳节点
update[1] = node12, node12->forward[1] = node17->forward[1] = node19
update[2] = node9, node9->forward[2] = node17->forward[2] = node25
update[3] = node6, node6->forward[3] = node25 > node 17, break
(3) free node17
(4) 因为node17的level只有2,所以不会导致List->level的变化
我们再尝试删除一下node6,过程为:
(1)完成search操作后的update向量为
update[4] = list->header
update[3] = list->header
update[2] = list->header
update[1] = node3
(2) 遍历更新前向节点的下k跳节点
update[1] = node3, node3->forward[1] = node6->forward[1] = node7
update[2] = list->header, list->header->forward[2] = node6->forward[2] = node9
update[3] = list->header, list->header->forward[3] = node6->forward[3] = node25
update[4] = list->header, list->header->forward[4] = node6->forward[4] = NIL, break
(3) free node6
(4) 更新list->level
此时,list->level == 4
for i:= 4 to 1
list->header[4] == NIL, list->level = 4-1 = 3
list->header[3] == node25, break
此时,list->level == 3
删除算法伪代码
选择一个随机level (Choosing a Random Level)
计算随机level的伪代码(按上述例子,p=1/2)
因为p=1/2,所以我们可以用抛硬币的场景来模拟一下。
lvl的初始值是1,而想让lvl+1的条件是我必须能random到一个小于0.5的值(假设为抛到硬币的正面吧)
那么,若我希望lvl == 2,则意味着我必须两次抛到硬币的正面才行,那么概率就是0.5*0.5
若我希望lvl == 3, 则必须连续三次抛到硬币的正面,则概率为 0.5*0.5*0.5
依次类推,若希望得到更高的lvl,则需要连续抛到正面的次数要求越多,概率也随之降低。
应该从第几层检索(At what level do we start a search? Defining L(n))
按照刚刚的随机level生成器,对于一个有16个节点的linked list,我们可能得到这样的结果
level1 有9个节点
level2 有3个节点
level3 有3个节点
level14 有1个节点 (概率非常非常低,需要连续13次抛出硬币的正面)
若我们每次搜索都从level 14开始,那么会有很多无用功。(因为level 4-13都是只有一个节点)。
那么,我们应该从哪个level开始做搜索呢?
理想情况下,为下文描述方便此公式简写为L(n)
当list中有一个元素拥有异常巨大的level时,我们通常有如下几种应对方法.
策略一: Don’t worry, be happy.
就按list->level来搜索,某个节点的level远远大于L(n)的概率是极地极低的。
策略二: Use less than you are given.
尽管对于level14的node,我们有14个forward指针,但是可以将其优化到仅使用L(n)个指针。不过这个优化的成本很高,但收益很小,因此不推荐用这种方法。
策略三:Fix the dice
把筛子固定下来,对于每个newNode,都简单粗暴的将它的level定义为当前最大level+1。
但是这样会导致算法的随机性被打破。
(那能否每次随机生成level时,maxLevel等于当前最大有效level+1呢?这样一来,可以缓慢的趋近预设的MAXLevel,而不至于中间出现很多断层的level)
MaxLevel取多少合适(Determining MaxLevel)
直接给结论吧
MaxLevel = L(N) (where N is an upper bound on the number of elements in a skip list).
对于p=1/2 , MaxLevel = 16的跳表,可以存储216个元素。
Skip Lists: A Probabilistic Alternative to Balanced Trees 阅读笔记的更多相关文章
- 讲讲跳跃表(Skip Lists)
跳跃表(Skip Lists)是一种有序的数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的.在大部分情况下,跳跃表的效率可以和平衡树相媲美,并且在实现上比平衡树要更为 ...
- 【转载】 《Human-level concept learning through probabilistic program induction》阅读笔记
原文地址: https://blog.csdn.net/ln1996/article/details/78459060 --------------------- 作者:lnn_csdn 来源:CSD ...
- 论文阅读笔记五十三:Libra R-CNN: Towards Balanced Learning for Object Detection(CVPR2019)
论文原址:https://arxiv.org/pdf/1904.02701.pdf github:https://github.com/OceanPang/Libra_R-CNN 摘要 相比模型的结构 ...
- 论文阅读笔记(二十二)【CVPR2017】:See the Forest for the Trees: Joint Spatial and Temporal Recurrent Neural Networks for Video-based Person Re-identification
Introduction 在视频序列中,有些帧由于被严重遮挡,需要被尽可能的“忽略”掉,因此本文提出了时间注意力模型(temporal attention model,TAM),注重于更有相关性的帧. ...
- Skip list--reference wiki
In computer science, a skip list is a data structure that allows fast search within an ordered seque ...
- 用golang实现常用算法与数据结构——跳跃表(Skip list)
背景 最近在学习 redis,看到redis中使用 了skip list.在网上搜索了一下发现用 golang 实现的 skip list 寥寥无几,性能和并发性也不是特别好,于是决定自己造一个并发安 ...
- 探索Skip List (跳跃表)
附William Pugh的论文 Skip Lists: A Probabilistic Alternative to Balanced Trees 写在前面 以下内容针对的是Skip List的插入 ...
- Redis入门指南(第2版) Redis设计思路学习与总结
https://www.qcloud.com/community/article/222 宋增宽,腾讯工程师,16年毕业加入腾讯,从事海量服务后台设计与研发工作,现在负责QQ群后台等项目,喜欢研究技术 ...
- [转载] 跳表SkipList
原文: http://www.cnblogs.com/xuqiang/archive/2011/05/22/2053516.html leveldb中memtable的思想本质上是一个skiplist ...
随机推荐
- win10 home安装docker快速攻略
本文适用于win10 Home用户,专业版和企业版直接见官网.win7版本见Docker Toolbox. 安装清单 软件 说明 Docker Desktop Installer 步骤介绍页:http ...
- Linux nginx 安装 启动
nginx下载地址:https://nginx.org/download/ ## 解压 tar -zxvf nginx-1.9.9.tar.gz ##进入nginx目录 cd nginx-1.9.9 ...
- MeteoInfoLab脚本示例:计算温度平流
需要温度和风场U/V分量格点数据,计算中主要用到cdiff函数,结果用GrADS验证一致.脚本程序: print 'Open data files...' f_air = addfile('D:/Te ...
- MeteoInfoLab脚本示例:加载地图图层
应用最广泛的的地图数据应该是shape格式,网络上有很多免费下载资源.MeteoInfoLab中读取shape文件的函数是shaperead,参数即文件名,返回数据包含图形和属性信息的图层对象.矢量图 ...
- day48 Pyhton 数据库Mysql 05
一内容回顾 insert insert into 表名 (字段名) values (值) insert into 表名 values (有多少个字段写多少个值) insert into 表名 val ...
- 微信聊天记录导出为csv,并生成词云图
微信聊天记录生成特定图片图云 首先贴上github地址 https://github.com/ghdefe/WechatRecordToWordCloud 来个效果图 提取聊天记录到csv参考教程 h ...
- kali 安裝虛擬機VMware
0x00前言 由於之前已經安裝過虛擬機,這次爲了寫博客又重新安裝了一邊,有些地方直接按照之前的默認的設置來了,省了設置,中間又換了一個實驗環境.完成了文章中的演示,整個過程多次實驗是沒問題的,若有疑問 ...
- docker启动服务---------------rabbitmq
1.进入docker hub镜像仓库地址:https://hub.docker.com/ 2.搜索rabbitMq,进入官方的镜像,可以看到以下几种类型的镜像:我们选择带有"mangemen ...
- git reset 与 git revert的区别?
一,git reset的功能: 该命令修改HEAD的位置,即将HEAD指向的位置改变为之前存在的某个版本, 说明: 修改后,push到远程仓库时需要使用"git push -f"提 ...
- docker19.03限制容器使用的内存资源
一,指定内存大小的参数: [root@localhost liuhongdi]# docker run -idt --name kafka2 --hostname kafka2 -m 200M --m ...