跳表是一个随机化的数据结构,实质是一种可以进行二分查找的有序链表。
跳表在原有的有序链表上增加了多级索引,通过索引来实现快速查询。
跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能。
先来看看二分查找:时间复杂度O(log2n)
def bin_search(data_list, val):
low = 0 # 最小数下标
high = len(data_list) - 1 # 最大数下标
while low <= high:
mid = (low + high) // 2 # 中间数下标
if data_list[mid] == val: # 如果中间数下标等于val, 返回
return mid
elif data_list[mid] > val: # 如果val在中间数左边, 移动high下标
high = mid - 1
else: # 如果val在中间数右边, 移动low下标
low = mid + 1
return # val不存在, 返回None
ret = bin_search(list(range(1, 10)), 3)
print(ret)
用数组来模拟二分查找 很方便,连续的内存空间,利用偏移量就能取出值 然后比较。。。
跳跃表的原理很好理解,看了网上的实现,写的比较难懂,可能我功力有限,索性就自己写一套
import random
class SkipNode(object):
def __init__(self,data):
self.data = data
self.next = None
self.down = None
def __str__(self):
return "[%s]-" % self.data
class SkipList(object):
def __init__(self,maxlevel = 100):
self.maxlevel = maxlevel
self.level = 0
self.count = 0
self.head = None
def randomLevel(self):
level = 1
while random.randint(1,100) % 2 == 0 and level < self.maxlevel:
level += 1
return level
def insert(self,data):
path = []
rand_level = self.randomLevel()
print("rand_level:%s data:%s" % (rand_level,data))
start_cursor = None
if rand_level > self.level:
i = self.level
while i < rand_level:
node = SkipNode(-1)
node.down = self.head
self.head = node
i += 1
self.level = rand_level
start_cursor = self.head
else:
start_cursor = self.head
curlevel = self.level
while curlevel > rand_level:
start_cursor = start_cursor.down
curlevel -= 1
t = start_cursor
while t != None:
if t.next != None:
if data < t.next.data:
path.append(t)
t = t.down
else:
t = t.next
else:
path.append(t)
t = t.down
index = len(path) - 1
downtmp = None
while index >=0:
pn = path[index]
node = SkipNode(data)
node.next = pn.next
pn.next = node
node.down = downtmp
downtmp = node
index -= 1
pass
def search(self,data):
t = self.head
s = "search:" + str(data) + "-->"
while t != None:
s += str(t)
if t.data == data:
print(s)
return True
else:
if t.next:
if t.next.data > data:
t = t.down
else:
t = t.next
else:
t = t.down
print(s)
return False
def delete(self,data):
t = self.head
print("delete:" + str(data))
while t != None:
if t.next:
if t.next.data == data:
t.next = t.next.next
t = t.down
else:
if t.next.data < data:
t = t.next
else:
t = t.down
else:
t = t.down
pass
def dump(self):
head = self.head
while head:
s = ""
node = head
while node:
s = s + str(node)
node = node.next
print(s)
head = head.down
pass
插入insert 分析:
1:抛硬币 得出最大能到第几层rand_level,最下一层是1
2:如果rand_level 比 self.level 大,也就是说要产生新的索引层,当前的data是要从新加的层开始一直到最底层(1层) 每层都要加入这个索引。。有新的索引层产生就要生成一个新的行头结点,同时更新跳跃表的头结点
i = self.level
while i < rand_level:
node = SkipNode(-1)
node.down = self.head
self.head = node
i += 1
self.level = rand_level
start_cursor = self.head
如果不需要生成新的索引行,那就找到rand_level 这个数值对应的索引行的头结点
start_cursor = self.head
curlevel = self.level
while curlevel > rand_level:
start_cursor = start_cursor.down
curlevel -= 1
start_cursor 表示需要插入data结点 这一索引层的头结点。。。从start_cursor开始一直到第一层,每层插入data结点就好,由于是单链表,插入一个节点需要知道插入位置的前驱节点,所以要找到这些前驱节点
t = start_cursor
while t != None:
if t.next != None:
if data < t.next.data:
path.append(t)
t = t.down
else:
t = t.next
else:
path.append(t)
t = t.down
t.next 为None 表示已经到了行索引列表的末尾了,这个时候就要跳到下一层了。。
从上到下,把找到的所有节点 按照先后顺序放入到path 这个list当中,最后要做的工作就是 在这些节点的后边插入新节点就好。。。
index = len(path) - 1
downtmp = None
while index >=0:
pn = path[index]
node = SkipNode(data)
node.next = pn.next
pn.next = node
node.down = downtmp
downtmp = node
index -= 1
由于要设置down指针,所以逆序遍历这些节点就好了。。。。
搜索节点分析
当前节点值data和要查找的data 一样 那就不说了 这就是要找的节点。。。
如果data值不等,那就先看看下一个节点,如果next 不存在 直接跳到下一层,如果next存在,值比search data值小,那就next 继续查 否则就要跳到下一层,逻辑还是比较简单的。。
删除节点分析
删除节点和添加节点类似,要找到前驱节点,因为删除节点的时候,节点的前驱节点的next指针要更新的。。。。
如果当前节点t的next的data 和 data 想等,直接t.next = t.next.next 然后t = t.down 直接跳到下一层
如果当前节点t的next的data和data 不等,就要判断大小关系,next.data < data 可以继续next,否则就要在当前节点跳到下一层。。。
def main():
import random
skip = SkipList(5)
datalist = []
for i in range(30):
x = random.randint(1,1000)
if x not in datalist:
datalist.append(x)
for x in datalist:
skip.insert(x)
f = datalist[10]
skip.dump()
print(skip.search(f))
print("###########################")
skip.delete(f)
skip.dump()