python实现跳表SkipList的示例代码
作者:von Libniz 发布时间:2022-04-05 05:01:53
标签:python,跳表,SkipList
跳表
跳表,又叫做跳跃表、跳跃列表,在有序链表的基础上增加了“跳跃”的功能,由William Pugh于1990年发布,设计的初衷是为了取代平衡树(比如红黑树)。
Redis、LevelDB 都是著名的 Key-Value 数据库,而Redis中 的 SortedSet、LevelDB 中的 MemTable 都用到了跳表。
对比平衡树,跳表的实现和维护会更加简单,跳表的搜索、删除、添加的平均时间复杂度是 O(logn)。
跳表的结构如图所示:
可以发现,对于一个节点Node,其含有多个next指针,不同索引的next分别代表不同层次的下一个节点,下次是节点类Node的python定义:
class Node():
def __init__(self,key,value,level):
'''
:param level:每个node对应的nexts层数不同
'''
self.key=key
self.value=value
self.nexts=[None]*level#节点类型next指针,初始值为空
def __str__(self):
#return "[key:"+str(self.key)+", value:"+str(self.value)+" len:"+str(len(self.nexts))+"]"
return "["+str(self.key)+","+str(self.value)+","+str(len(self.nexts))+"]"
关于添加、删除、查找见一下完整代码:
'''
跳表 Skip List ,其初衷是为了替代红黑树
'''
import random
import mkl_random
import time
class SkipList():
def __init__(self):
#头节点不存储任何数据
self.MAX_LEVEL = 32 # 最大level层数
self.__first=SkipList.Node(None, None, self.MAX_LEVEL)#头节点
self.__level=0#实际的level层数
self.__size=0#Jiedian个数
self.__p=0.25#用于生成添加节点时的随机level
return
class Node():
def __init__(self,key,value,level):
'''
:param level:每个node对应的nexts层数不同
'''
self.key=key
self.value=value
self.nexts=[None]*level
def __str__(self):
#return "[key:"+str(self.key)+", value:"+str(self.value)+" len:"+str(len(self.nexts))+"]"
return "["+str(self.key)+","+str(self.value)+","+str(len(self.nexts))+"]"
def get(self,key):
'''
:param key:
:return: key对应的value
'''
self.keyCheck(key)
node=self.__first
for level in range(self.__level - 1,-1,-1):
#在该层查找,key大于节点的key向前查找
while node.nexts[level] and node.nexts[level].key<key:
node=node.nexts[level]
if node.nexts[level] and node.nexts[level].key==key:#相等则找到,否则向下寻找
return node.nexts[level].value
return None
def put(self,key,value):
'''
return:原来的value,原来不存在key则为空
'''
self.keyCheck(key)
prev=[None]*self.__level
node=self.__first
for i in range(self.__level - 1, -1, -1):
while node.nexts[i] and node.nexts[i].key<key:
node=node.nexts[i]
if node.nexts[i] and node.nexts[i].key==key:
oldValue=node.nexts[i].value
node.nexts[i].value=value
return oldValue
prev[i]=node#保存当前level小于key的node
newLevel=self.randomLevel()
newNode=SkipList.Node(key,value,newLevel)
for i in range(newLevel):
if i<self.__level:
newNode.nexts[i]=prev[i].nexts[i]
prev[i].nexts[i]=newNode
else:
self.__first.nexts[i]=newNode
self.__size+=1
self.__level=max(self.__level, newLevel)
return None
def remove(self,key):
'''
:return: 节点对应的value值,不存在则返回None
'''
self.keyCheck(key)
prev=[None]*self.__level
node=self.__first
flag=False#该节点是否被查找到
for i in range(self.__level - 1, -1, -1):
while node.nexts[i] and node.nexts[i].key<key:
node=node.nexts[i]
if node.nexts[i].key==key:
flag=True
prev[i]=node
if not flag:
return None
removedNode=node.nexts[0]#需要被删除的节点
for i in range(len(removedNode.nexts)):#该nexts一定小于等于prev的长度
prev[i].next[i]=removedNode.nexts[i]
self.__size-=1
newLevel=self.__level
while newLevel>0 and not self.__first.nexts[newLevel - 1]:
newLevel-=1
self.__level=newLevel
return removedNode.value
def keyCheck(self, key):
'''
限制传入key不能为空
'''
if key!=0 and not key:
raise AttributeError("key can not be None")
def size(self):
return self.__size
def isEmpty(self):
return self.__size == 0
def randomLevel(self):#生成一个随机的层数
level=1
while mkl_random.rand()<self.__p and level<self.MAX_LEVEL:
level+=1
return level
def __str__(self):
result=""
for i in range(self.__level - 1, -1, -1):
result+=str(i)
node = self.__first
while node.nexts[i]:
result+=str(node.nexts[i])
node=node.nexts[i]
result+='\n'
print("level:"+str(self.__level))
return result
def showFirst(self):
for item in self.__first.nexts:
print(item,end=' ')
print()
def timeCalculate(container, size:int):
begin=time.time()
for i in range(size):
if isinstance(container,dict):
container[i]= i * 3
else:
container.put(i, i * 3)
error_count = 0
for i in range(size):
if container.get(i) != i * 3:
#print("wrong " + str(i) + ":" + str(skipList.get(i)))
error_count+=1
end=time.time()
print(type(container))
print(f'error rate:{float(error_count) / size:0.5f}')
print(f'time cost:{float(end-begin)*1000:0.3f} ms')
if __name__=='__main__':
timeCalculate({},1000000)
timeCalculate(SkipList(),10000)
来源:https://blog.csdn.net/Demon_LMMan/article/details/119064581


猜你喜欢
- 关于MySQL的事务隔离级别,相信很多读者都不陌生,网商有很多种相关的文章,很多人对于各种隔离级别,以及不同的级别可以解决的一些读现象都是如
- 今天来分享python学习的一个小例子,使用python暴力破解mysql数据库,实现方式是通过UI类库tkinter实现可视化面板效果,在
- Exec sp_droplinkedsrvlogin ZYB,Null --删除映射(录与链接服务器上远程登录之间的映射) Exec sp_
- CSS3 + HTML5是未来的Web,它们都还没有正式到来,虽然不少浏览器已经开始对它们提供部分支持。本文介绍了5个CSS3技巧,可以帮你
- 前言在做项目中,网站前台或者后台有些数据需要在多个视图页面使用,例如用户基本信息数据,菜单展示数据。首先想到的是在每个控制器里传入这些需要的
- 0.摘要我们将一个list传入函数后,函数内部对实参修改后,形参也会随之改变。本文将主要介绍这种错误的现象、原因和解决方法。1.代码示例de
- 比如一个详细的简历信息页面分 1、个人资料 2、工作经验 3、项目经验 4、其他信息 4个表单。 一般的需求是允许用户单独提交其中任何一个表
- 在我之前解释了flask如何支持多线程主要通过两个类来实现,LocalStack和Local,在Local中有两个属性,__storage_
- 问题keras使用预训练模型vgg16分类,损失和准确度不变。细节:使用keras训练一个两类数据,正负比例1:3,在vgg16后添加了几个
- 主要步骤1.生成普通python数组(bytearray(),os.urandom())2.转换成numpy数组(numpy.array()
- 有时候写爬虫的时候需要将浏览器中的headers信息复制到Pycharm中使用,但是复制过来的格式不是需要的键值格式,挨个手动敲键盘太累,毕
- 本文较为详细的讲述了Python实现远程调用MetaSploit的方法,对Python的学习来说有很好的参考价值。具体实现方法如下:(1)安
- 在前面的博文中,我们介绍了如何通过软件模拟实现共享磁盘(https://www.jb51.net/network/592807.html),
- 实际操作中我们经常需要寻找数据的某行或者某列,这里介绍我在使用Pandas时用到的两种方法:iloc和loc。loc:通过行、列的名称或标签
- 常规通过迭代或set方法,都无法保证去重后的顺序问题如下,我们可以通过列表的索引功能,对set结果进行序列化old_list=["
- sql调优的几种方式:避免使用select *、用union all 代替union、小表驱动大表、批量操作、多用limit、in中值太多、
- javascript的字符集:javascript程序是使用Unicode字符集编写的。Unicode是ASCII和Latin-1的超集,并
- async官方DOC介绍node安装npm install async --save使用var async = require('a
- 用python另一个抢票神器,你get到了吗?2017年时间飞逝,转眼间距离2018年春节还有不到1个月的时间,还在为抢不到火车票发愁吗?作
- Vue baseurl配置最近的一个vue项目,没有config文件夹,配置baseurl废了很大劲,终于找到了方法,感天动地o(╥﹏╥)o