Skip lists are a data structure that can be used in place of balanced trees. Skip lists use probabilistic balancing rather than strictly enforced balancing and as a result the algorithms for insertion and deletion in skip lists are much simpler and significantly faster than equivalent algorithms for balanced trees.
–William Pugh
装在整理至:
跳表是由William Pugh发明的,上面的引言就是他给出的解释。跳表是一种随机化的数据结构,目前开源软件 Redis 和 LevelDB 都有用到它,它的效率和红黑树以及 AVL 树不相上下,但跳表的原理相当简单,只要你能熟练操作链表,就能轻松实现一个 SkipList。
跳跃表结构体
1 | typedef struct node //每个节点的数据结构 |
2 | { |
3 | keyType key; // key值 |
4 | valueType value; // value值 |
5 | struct node *next[1]; // 后继指针数组,柔性数组 可实现结构体的变长 |
6 | } Node; |
7 | typedef struct skip_list //跳表结构 |
8 | { |
9 | int level; // 最大层数 |
10 | Node *head; //指向头结点 |
11 | } skip_list; |
柔性数组
sizeof(Node*)))柔性数组既数组大小待定的数组, C语言中结构体的最后一个元素可以是大小未知的数组,也就是所谓的0长度,所以我们可以用结构体来创建柔性数组。柔性数组既数组大小待定的数组, C语言中结构体的最后一个元素可以是大小未知的数组,也就是所谓的0长度,所以我们可以用结构体来创建柔性数组。
1 |
|
跳跃表的插入
跳表的插入总结起来需要三步:
- 查找到待插入位置, 每层跟新update数组;
- 需要随机产生一个层数;
- 从高层至下插入,与普通链表的插入完全相同;
1 | //插入元素的时候元素所占有的层数完全是随机算法 |
2 | int randomLevel() |
3 | { |
4 | int level=1; |
5 | while (rand()%2) |
6 | level++; |
7 | level=(MAX_L>level)? level:MAX_L; |
8 | return level; |
9 | } |
10 | /* |
11 | step1:查找到在每层待插入位置,跟新update数组 |
12 | step2:需要随机产生一个层数 |
13 | step3:从高层至下插入,与普通链表的插入完全相同。 |
14 | */ |
15 | bool insert(skip_list *sl, keyType key, valueType val) |
16 | { |
17 | Node *update[MAX_L]; |
18 | Node *q=NULL,*p=sl->head;//q,p初始化 |
19 | int i=sl->level-1; |
20 | /******************step1*******************/ |
21 | //从最高层往下查找需要插入的位置,并更新update |
22 | //即把降层节点指针保存到update数组 |
23 | for( ; i>=0; --i) |
24 | { |
25 | while((q=p->next[i])&& q->key<key) |
26 | p=q; |
27 | update[i]=p; |
28 | } |
29 | if(q && q->key == key)//key已经存在的情况下 |
30 | { |
31 | q->value = val; |
32 | return true; |
33 | } |
34 | /******************step2*******************/ |
35 | //产生一个随机层数level |
36 | int level = randomLevel(); |
37 | //如果新生成的层数比跳表的层数大 |
38 | if(level>sl->level) |
39 | { |
40 | //在update数组中将新添加的层指向header |
41 | for(i=sl->level; i<level; ++i) |
42 | { |
43 | update[i]=sl->head; |
44 | } |
45 | sl->level=level; |
46 | } |
47 | //printf("%d\n", sizeof(Node)+level*sizeof(Node*)); |
48 | /******************step3*******************/ |
49 | //新建一个待插入节点,一层一层插入 |
50 | q=create_node(level, key, val); |
51 | if(!q) |
52 | return false; |
53 | |
54 | //逐层更新节点的指针,和普通链表插入一样 |
55 | for(i=level-1; i>=0; --i) |
56 | { |
57 | q->next[i]=update[i]->next[i]; |
58 | update[i]->next[i]=q; |
59 | } |
60 | return true; |
61 | } |
删除节点
1 | bool erase(skip_list *sl, keyType key) |
2 | { |
3 | Node *update[MAX_L]; |
4 | Node *q=NULL, *p=sl->head; |
5 | int i = sl->level-1; |
6 | for(; i>=0; --i) |
7 | { |
8 | while((q=p->next[i]) && q->key < key) |
9 | { |
10 | p=q; |
11 | } |
12 | update[i]=p; |
13 | } |
14 | //判断是否为待删除的key |
15 | if(!q || (q&&q->key != key)) |
16 | return false; |
17 | |
18 | //逐层删除与普通链表删除一样 |
19 | for(i=sl->level-1; i>=0; --i) |
20 | { |
21 | if(update[i]->next[i]==q)//删除节点 |
22 | { |
23 | update[i]->next[i]=q->next[i]; |
24 | //如果删除的是最高层的节点,则level-- |
25 | if(sl->head->next[i]==NULL) |
26 | sl->level--; |
27 | } |
28 | } |
29 | free(q); |
30 | q=NULL; |
31 | return true; |
32 | } |
查找操作
1 | valueType *search(skip_list *sl, keyType key) |
2 | { |
3 | Node *q,*p=sl->head; |
4 | q=NULL; |
5 | int i=sl->level-1; |
6 | for(; i>=0; --i) |
7 | { |
8 | while((q=p->next[i]) && q->key<key) |
9 | { |
10 | p=q; |
11 | } |
12 | if(q && key==q->key) |
13 | return &(q->value); |
14 | } |
15 | return NULL; |
16 | } |