Skip to content

Commit 5a6cf7d

Browse files
Merge pull request #325 from Song2017/master
skip list
2 parents e2201fe + 2fdca2e commit 5a6cf7d

File tree

1 file changed

+118
-0
lines changed

1 file changed

+118
-0
lines changed
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import random
2+
3+
4+
class SkipListNode(object):
5+
def __init__(self, val, high=1):
6+
# 节点存储的值
7+
self.data = val
8+
# 节点对应索引层的深度
9+
self.deeps = [None] * high
10+
11+
12+
class SkipList(object):
13+
"""
14+
An implementation of skip list.
15+
The list stores positive integers without duplicates.
16+
跳表的一种实现方法。
17+
跳表中储存的是正整数,并且储存的是不重复的。
18+
Author: Ben
19+
"""
20+
21+
def __init__(self):
22+
# 索引层的最大深度
23+
self.__MAX_LEVEL = 16
24+
# 跳表的高度
25+
self._high = 1
26+
# 每一索引层的首节点, 默认值为None
27+
self._head = SkipListNode(None, self.__MAX_LEVEL)
28+
29+
def find(self, val):
30+
cur = self._head
31+
# 从索引的顶层, 逐层定位要查找的值
32+
# 索引层上下是对应的, 下层的起点是上一个索引层中小于插入值的最大值对应的节点
33+
for i in range(self._high - 1, -1, -1):
34+
# 同一索引层内, 查找小于插入值的最大值对应的节点
35+
while cur.deeps[i] and cur.deeps[i].data < val:
36+
cur = cur.deeps[i]
37+
38+
if cur.deeps[0] and cur.deeps[0].data == val:
39+
return cur.deeps[0]
40+
return None
41+
42+
def insert(self, val):
43+
'''
44+
新增时, 通过随机函数获取要更新的索引层数,
45+
要对低于给定高度的索引层添加新结点的指针
46+
'''
47+
high = self.randomLevel()
48+
if self._high < high:
49+
self._high = high
50+
# 申请新结点
51+
newNode = SkipListNode(val, high)
52+
# cache用来缓存对应索引层中小于插入值的最大节点
53+
cache = [self._head] * high
54+
cur = self._head
55+
56+
# 在低于随机高度的每一个索引层寻找小于插入值的节点
57+
for i in range(high - 1, -1, -1):
58+
# 每个索引层内寻找小于带插入值的节点
59+
# ! 索引层上下是对应的, 下层的起点是上一个索引层中小于插入值的最大值对应的节点
60+
while cur.deeps[i] and cur.deeps[i].data < val:
61+
cur = cur.deeps[i]
62+
cache[i] = cur
63+
64+
# 在小于高度的每个索引层中插入新结点
65+
for i in range(high):
66+
# new.next = prev.next \ prev.next = new.next
67+
newNode.deeps[i] = cache[i].deeps[i]
68+
cache[i].deeps[i] = newNode
69+
70+
def delete(self, val):
71+
'''
72+
删除时, 要将每个索引层中对应的节点都删掉
73+
'''
74+
# cache用来缓存对应索引层中小于插入值的最大节点
75+
cache = [None] * self._high
76+
cur = self._head
77+
# 缓存每一个索引层定位小于插入值的节点
78+
for i in range(self._high - 1, -1, -1):
79+
while cur.deeps[i] and cur.deeps[i].data < val:
80+
cur = cur.deeps[i]
81+
cache[i] = cur
82+
# 如果给定的值存在, 更新索引层中对应的节点
83+
if cur.deeps[0] and cur.deeps[0].data == val:
84+
for i in range(self._high):
85+
if cache[i].deeps[i] and cache[i].deeps[i].data == val:
86+
cache[i].deeps[i] = cache[i].deeps[i].deeps[i]
87+
88+
def randomLevel(self, p=0.25):
89+
'''
90+
#define ZSKIPLIST_P 0.25 /* Skiplist P = 1/4 */
91+
https://github.com/antirez/redis/blob/unstable/src/t_zset.c
92+
'''
93+
high = 1
94+
for _ in range(self.__MAX_LEVEL - 1):
95+
if random.random() < p:
96+
high += 1
97+
return high
98+
99+
def __repr__(self):
100+
vals = []
101+
p = self._head
102+
while p.deeps[0]:
103+
vals.append(str(p.deeps[0].data))
104+
p = p.deeps[0]
105+
return '->'.join(vals)
106+
107+
108+
if __name__ == '__main__':
109+
sl = SkipList()
110+
for i in range(100):
111+
sl.insert(i)
112+
print(sl)
113+
p = sl.find(7)
114+
print(p.data)
115+
sl.delete(37)
116+
print(sl)
117+
sl.delete(37.5)
118+
print(sl)

0 commit comments

Comments
 (0)