Skip List

来源:互联网 发布:java 字段 编辑:程序博客网 时间:2024/06/05 03:49

什么是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,减去高出的部分。

0 0
原创粉丝点击