什么是skip list?
“Skip lists are a data structure that can be used in place ofbalanced trees. Skip lists use probabilistic balancing rather than strictlyenforced balancing and as a result the algorithms for insertion and deletion inskip lists are much simpler and significantly faster than equivalent algorithmsfor balanced trees.“
如上为skip list作者William Pugh对skip list的解释,跳表是一种可以替代平衡树的数据结构,跳表使用的平衡算法并不是严格要求的平衡,这使插入和删除操作是更简单,并且明显比平衡树快速。
跳表数据结构
如上图中的a链表,是一个有序链表,如果从中找一个节点,最多可能需要遍历每一个节点,那么时间复杂度就是O(n);n为链表中节点的个数。
如上图中的b链表,每隔一个节点多了一层指针,指向它前面第二2个节点,那么我们要查找一个节点,最多可能需要遍历(n/2)+ 1个节点,时间复杂度就是O(n/2);
如上图中的c链表,在b链表的基础上多了一层指针,每第四个节点,指向它前面第四个节点,那么我们要查找一个节点,最多可能需要遍历(n/4)+ 2个节点,时间复杂度就是O(n/4);
如上图中的d链表,如果每第2^i个节点有指针指向它前面第2^i个节点,那么查找某个节点的时间复杂度可以降低到O(logn),这种结构查询起来很快速,但是插入和删除确是不切实际的。
一个节点有k个向前的指针,那么就称这个节点为k层节点,每个节点的层数是随机产生的,产生level 1的概率是50%,level2的概率是25%,level 3的概率是12.5%……,每个节点的层数的产生要保证采用这种相同的机制;每个节点的第i层指针,指向下一个至少有i层的节点。因为会滑过一些中间节点,所以称为跳表。
跳表算法
查找(伪码如下)
Search(list, searchKey)
x :=list→header
--loop invariant: x → key < searchKey
for i := list→level downto 1 do
while x →forward[i]→ key < searchKey do
x := x →forward[i]
-- x→ key < searchKey ≤ x→ forward[1] → key
x:= x →forward[1]
if x →key = searchKey then return x → value
else return failure
从顶层开始找,每层中寻找比目标小的最大的一个节点,然后转到下一层,然后向前移动一个节点,如果相等就代表找到了,如果不相等,就代表没有这个节点。
插入(伪码如下)
Insert(list, searchKey, newValue)
local update[1..MaxLevel]
x :=list→header
for i := list→level downto 1 do
while x →forward[i]→ key < searchKey do
x := x →forward[i]
-- x →key < searchKey ≤ x →forward[i] →key
update[i] := x
x:= x →forward[1]
if x →key = searchKey then x → value := newValue
else
lvl := randomLevel()
if lvl > list → level then
for i := list→level + 1 to lvl do
update[i] := list→header
list→ level := lvl
x := makeNode(lvl, searchKey, value)
for i := 1 to level do
x →forward[i] := update[i] →forward[i]
update[i] →forward[i] := x
从顶层开始找,每层中寻找比目标小的最大的一个节点,并记录该节点到update数组,如果新节点的层数大于之前的,则更新list→level,并将list→header放到update数组,其实update数组存放的就是i层中指向目标节点(正插入)的前一个节点,所以最后就是循环每一层,将每层中指向目标的前一节点的对应指针指向目标节点,目标节点的下一个节点指向前一个节点的下一个节点。
删除(伪码如下)
Delete(list, searchKey)
local update[1..MaxLevel]
x := list→header
for i := list→ level downto 1 do
while x→forward[i]→ key < searchKey do
x := x →forward[i]
update[i]:= x
x := x →forward[1]
if x →key = searchKey then
for i := 1 to list → level do
if update[i] →forward[i] ≠ x then break
update[i]→forward[i] := x→ forward[i]
free(x)
while list→level > 1 and
list→header→ forward[list →level] = NIL do
list→level := list →level – 1
从顶层开始找,每层中寻找比目标小的最大的一个节点,并记录该节点到update数组,循环每层,将update中指向目标节点(被删除的节点)的指针指向目标节点的下一个节点;如果被删节点的层数是最高的,就要更新list→level,减去高出的部分。