python實(shí)現(xiàn)跳表SkipList的示例代碼
跳表
跳表,又叫做跳躍表、跳躍列表,在有序鏈表的基礎(chǔ)上增加了“跳躍”的功能,由William Pugh于1990年發(fā)布,設(shè)計(jì)的初衷是為了取代平衡樹(比如紅黑樹)。
Redis、LevelDB 都是著名的 Key-Value 數(shù)據(jù)庫,而Redis中 的 SortedSet、LevelDB 中的 MemTable 都用到了跳表。
對比平衡樹,跳表的實(shí)現(xiàn)和維護(hù)會更加簡單,跳表的搜索、刪除、添加的平均時間復(fù)雜度是 O(logn)。
跳表的結(jié)構(gòu)如圖所示:

可以發(fā)現(xiàn),對于一個節(jié)點(diǎn)Node,其含有多個next指針,不同索引的next分別代表不同層次的下一個節(jié)點(diǎn),下次是節(jié)點(diǎn)類Node的python定義:
class Node():
def __init__(self,key,value,level):
'''
:param level:每個node對應(yīng)的nexts層數(shù)不同
'''
self.key=key
self.value=value
self.nexts=[None]*level#節(jié)點(diǎn)類型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))+"]"
關(guān)于添加、刪除、查找見一下完整代碼:
'''
跳表 Skip List ,其初衷是為了替代紅黑樹
'''
import random
import mkl_random
import time
class SkipList():
def __init__(self):
#頭節(jié)點(diǎn)不存儲任何數(shù)據(jù)
self.MAX_LEVEL = 32 # 最大level層數(shù)
self.__first=SkipList.Node(None, None, self.MAX_LEVEL)#頭節(jié)點(diǎn)
self.__level=0#實(shí)際的level層數(shù)
self.__size=0#Jiedian個數(shù)
self.__p=0.25#用于生成添加節(jié)點(diǎn)時的隨機(jī)level
return
class Node():
def __init__(self,key,value,level):
'''
:param level:每個node對應(yīng)的nexts層數(shù)不同
'''
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對應(yīng)的value
'''
self.keyCheck(key)
node=self.__first
for level in range(self.__level - 1,-1,-1):
#在該層查找,key大于節(jié)點(diǎn)的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#保存當(dāng)前l(fā)evel小于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: 節(jié)點(diǎn)對應(yīng)的value值,不存在則返回None
'''
self.keyCheck(key)
prev=[None]*self.__level
node=self.__first
flag=False#該節(jié)點(diǎn)是否被查找到
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]#需要被刪除的節(jié)點(diǎn)
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):#生成一個隨機(jī)的層數(shù)
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)
到此這篇關(guān)于python實(shí)現(xiàn)跳表SkipList的文章就介紹到這了,更多相關(guān)python 跳表SkipList內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
twilio python自動撥打電話,播放自定義mp3音頻的方法
今天小編就為大家分享一篇twilio python自動撥打電話,播放自定義mp3音頻的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-08-08
matlab調(diào)用python的各種方法舉例子詳解
為了發(fā)揮matlab的繪圖優(yōu)勢+原先python寫好的功能組合方式,下面這篇文章主要給大家介紹了關(guān)于matlab調(diào)用python的各種方法,需要的朋友可以參考下2023-09-09
OpenCV每日函數(shù)之BarcodeDetector類條碼檢測器
OpenCV在V4.5.3版本的contrib包中提供了一個barcode::BarcodeDetector類,用于條形碼的識別,這篇文章主要介紹了OpenCV每日函數(shù)?BarcodeDetector條碼檢測器,需要的朋友可以參考下2022-06-06
使用apidoc管理RESTful風(fēng)格Flask項(xiàng)目接口文檔方法
下面小編就為大家分享一篇使用apidoc管理RESTful風(fēng)格Flask項(xiàng)目接口文檔方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-02-02
python3使用urllib示例取googletranslate(谷歌翻譯)
這篇文章主要介紹了使用urllib取googletranslate(谷歌翻譯)的示例,通過這個谷歌翻譯示例學(xué)習(xí)python3中urllib的使用方法,2014-01-01
Django uwsgi Nginx 的生產(chǎn)環(huán)境部署詳解
這篇文章主要介紹了Django uwsgi Nginx 的生產(chǎn)環(huán)境部署詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-02-02
使用AJAX和Django獲取數(shù)據(jù)的方法實(shí)例
這篇文章主要給大家介紹了關(guān)于使用AJAX和Django獲取數(shù)據(jù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10

