SkipList跳表基本原理

 

为什么选择跳表

目前经常使用的平衡数据结构有:B树,红黑树,AVL树,Splay Tree, Treep等。

想象一下,给你一张草稿纸,一只笔,一个编辑器,你能立即实现一颗红黑树,或者AVL树

出来吗? 很难吧,这需要时间,要考虑很多细节,要参考一堆算法与数据结构之类的树,

还要参考网上的代码,相当麻烦。

用跳表吧,跳表是一种随机化的数据结构,目前开源软件 Redis 和 LevelDB 都有用到它,

它的效率和红黑树以及 AVL 树不相上下,但跳表的原理相当简单,只要你能熟练操作链表,

就能轻松实现一个 SkipList。

有序表的搜索

考虑一个有序表:

clip_image001

从该有序表中搜索元素 < 23, 43, 59 >,需要比较的次数分别为 < 2, 4, 6 >,总共比较的次数

为 2 + 4 + 6 = 12 次。有没有优化的算法吗? 链表是有序的,但不能使用二分查找。类似二叉

搜索树,我们把一些节点提取出来,作为索引。得到如下结构:

clip_image002

这里我们把 < 14, 34, 50, 72 >提取出来作为一级索引,这样搜索的时候就可以减少比较次数了。

我们还可以再从一级索引提取一些元素出来,作为二级索引,变成如下结构:

clip_image003

这里元素不多,体现不出优势,如果元素足够多,这种索引结构就能体现出优势来了。

这基本上就是跳表的核心思想,其实也是一种通过“空间来换取时间”的一个算法,通过在每个节点中增加了向前的指针,从而提升查找的效率。

跳表

下面的结构是就是跳表:

其中 -1 表示 INT_MIN, 链表的最小值,1 表示 INT_MAX,链表的最大值。

clip_image005

跳表具有如下性质:

(1) 由很多层结构组成

(2) 每一层都是一个有序的链表

(3) 最底层(Level 1)的链表包含所有元素

(4) 如果一个元素出现在 Level i 的链表中,则它在 Level i 之下的链表也都会出现。

(5) 每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素。

跳表的搜索

clip_image007

例子:查找元素 117

(1) 比较 21, 比 21 大,往后面找

(2) 比较 37,   比 37大,比链表最大值小,从37 的下面一层开始找

(3) 比较 71,  比 71 大,比链表最大值小,从 71 的下面一层开始找

(4) 比较 85, 比 85 大,从后面找

(5) 比较 117, 等于 117, 找到了节点。

具体的搜索算法如下:

C代码 clip_image009

1.

3. find(x)  

4. { 

5.    p = top; 

6. while (1) { 

7. while (p->next->key< x) 

8.            p = p->next; 

9. if (p->down ==NULL)  

10. return p->next; 

11.        p = p->down; 

12.    

13. } 

跳表的插入

先确定该元素要占据的层数 K(采用丢硬币的方式,这完全是随机的)

然后在 Level 1 ... Level K 各个层的链表都插入元素。

例子:插入 119, K = 2

clip_image011

如果 K 大于链表的层数,则要添加新的层。

例子:插入 119, K = 4

clip_image013

丢硬币决定 K

插入元素的时候,元素所占有的层数完全是随机的,通过一下随机算法产生:

C代码 clip_image009[1]

1. int random_level() 

2. { 

3.    K = 1; 

4.

5. while (random(0,1)) 

6.        K++; 

7.

8. return K; 

9. } 

相当与做一次丢硬币的实验,如果遇到正面,继续丢,遇到反面,则停止,

用实验中丢硬币的次数 K 作为元素占有的层数。显然随机变量 K 满足参数为 p = 1/2 的几何分布,

K 的期望值 E[K] = 1/p = 2. 就是说,各个元素的层数,期望值是 2 层。

跳表的高度。

n 个元素的跳表,每个元素插入的时候都要做一次实验,用来决定元素占据的层数 K,

跳表的高度等于这 n 次实验中产生的最大 K,待续。。。

跳表的空间复杂度分析

根据上面的分析,每个元素的期望高度为 2, 一个大小为 n 的跳表,其节点数目的

期望值是 2n。

跳表的删除

在各个层中找到包含 x 的节点,使用标准的 delete from list 方法删除该节点。

例子:删除 71

clip_image015

 

源地址:http://kenby.iteye.com/blog/1187303



跳表(skiplist)的代码实现

跳表(skiplist)是一个非常优秀的数据结构,实现简单,插入、删除、查找的复杂度均为O(logN)。LevelDB的核心数据结构是用跳表实现的,redis的sorted set数据结构也是有跳表实现的。

其结构如下所示:

所有操作均从上向下逐层查找,越上层一次next操作跨度越大。其实现是典型的空间换时间。

具体的细节,可参考维基百科http://en.wikipedia.org/wiki/Skip_list

 

本文作者将redis的sorted set代码进行整理,将跳表部分的实现抽取出来,供参考。

skiplist.h

复制代码
 1 #ifndef __SKIPLIST_H
 2 #define __SKIPLIST_H
 3 
 4 #define SKIPLIST_MAXLEVEL 8 
 5 
 6 typedef struct skiplistNode {
 7     double score;
 8     struct skiplistNode *backward;
 9     struct skiplistLevel {
10         struct skiplistNode *forward;
11     }level[];
12 }skiplistNode;
13 
14 typedef struct skiplist {
15     struct skiplistNode *header, *tail;
16     unsigned long length;
17     int level;
18 }skiplist;
19 
20 #endif
复制代码

skiplist.c

复制代码
  1 #include "skiplist.h"
  2 #include <stdlib.h>
  3 #include <stdio.h>
  4 
  5 skiplistNode *slCreateNode(int level, double score) {
  6     skiplistNode * sn = malloc(sizeof(*sn) + level*sizeof(struct skiplistLevel));
  7     sn->score = score;
  8     return sn;
  9 }
 10 
 11 skiplist *slCreate(void) {
 12     int j;
 13     skiplist *sl;
 14 
 15     sl = malloc(sizeof(*sl));
 16     sl->level = 1;
 17     sl->length = 0;
 18     sl->header = slCreateNode(SKIPLIST_MAXLEVEL, 0);
 19     for(j = 0; j < SKIPLIST_MAXLEVEL; j++) {
 20         sl->header->level[j].forward = NULL;
 21     }
 22     sl->header->backward = NULL;
 23     sl->tail = NULL;
 24     return sl;
 25 }
 26 
 27 void slFreeNode(skiplistNode *sn) {
 28     free(sn);
 29 }
 30 
 31 void slFree(skiplist *sl) {
 32     skiplistNode *node = sl->header->level[0].forward, *next;
 33 
 34     free(sl->header);
 35     while(node) {
 36         next = node->level[0].forward;
 37         slFreeNode(node);
 38         node = next;
 39     }
 40     free(sl);
 41 }
 42 
 43 int slRandomLevel(void) {
 44     int level = 1;
 45     while((rand()&0xFFFF) < (0.5 * 0xFFFF)) 
 46         level += 1;
 47     return (level < SKIPLIST_MAXLEVEL) ? level : SKIPLIST_MAXLEVEL;
 48 }
 49 
 50 skiplistNode *slInsert(skiplist *sl, double score) {
 51     skiplistNode *update[SKIPLIST_MAXLEVEL];
 52     skiplistNode *node;
 53 
 54     node = sl->header;
 55     int i, level;
 56     for ( i = sl->level-1; i >= 0; i--) {
 57         while(node->level[i].forward && node->level[i].forward->score < score) {
 58             node = node->level[i].forward;
 59         }
 60         update[i] = node;
 61     }
 62     level = slRandomLevel();
 63     if (level > sl->level) {
 64         for (i = sl->level; i< level ;i++) {
 65             update[i] = sl->header;
 66         }
 67         sl->level = level;
 68     }
 69     node = slCreateNode(level, score);
 70     for (i = 0; i < level; i++) {
 71         node->level[i].forward = update[i]->level[i].forward;
 72         update[i]->level[i].forward = node;
 73     }
 74 
 75     node->backward = (update[0] == sl->header? NULL : update[0]);
 76     if (node->level[0].forward)
 77         node->level[0].forward->backward = node;
 78     else
 79         sl->tail = node;
 80     sl->length++;
 81     return node;
 82 }
 83 
 84 void slDeleteNode(skiplist *sl, skiplistNode *x, skiplistNode **update){
 85     int i;
 86     for (i = 0; i < sl->level; i++) {
 87         if (update[i]->level[i].forward == x) {
 88             update[i]->level[i].forward = x->level[i].forward;
 89         }
 90     }
 91     if (x->level[0].forward) {
 92         x->level[0].forward->backward = x->backward;
 93     } else {
 94         sl->tail = x->backward;
 95     }
 96     while (sl->level > 1 && sl->header->level[sl->level-1].forward == NULL) 
 97         sl->level--;
 98     sl->length--;
 99 }
100 
101 int slDelete(skiplist *sl, double score) {
102     skiplistNode *update[SKIPLIST_MAXLEVEL], *node;
103     int i;
104 
105     node = sl->header;
106     for(i = sl->level-1; i >= 0; i--) {
107         while (node->level[i].forward && node->level[i].forward->score < score) {
108             node = node->level[i].forward;
109         }
110         update[i] = node;
111     }
112     node = node->level[0].forward;
113     if (node && score == node->score) {
114         slDeleteNode(sl, node, update);
115         slFreeNode(node);
116         return 1;
117     } else {
118         return 0;
119     }
120     return 0;
121 }
122 
123 int slSearch(skiplist *sl, double score) {
124     skiplistNode *node;
125     int i;
126 
127     node = sl->header;
128     for (i = sl->level-1; i >= 0 ;i--) {
129         while(node->level[i].forward && node->level[i].forward->score < score) {
130             node = node->level[i].forward;
131         }
132     }
133     node = node->level[0].forward;
134     if (node && score == node->score) {
135         printf("Found %d\n",(int)node->score);
136         return 1;
137     } else {
138         printf("Not found %d\n", (int)score);
139         return 0;
140     }
141 }
142 
143 void slPrint(skiplist *sl) {
144     skiplistNode *node;
145     int i;
146     for (i = 0; i < SKIPLIST_MAXLEVEL; i++) {
147         printf("LEVEL[%d]: ", i);
148         node = sl->header->level[i].forward;
149         while(node) {
150             printf("%d -> ", (int)(node->score));
151             node = node->level[i].forward;
152         }
153         printf("NULL\n");
154     }
155 }
156 
157 #ifdef SKIP_LIST_TEST_MAIN
158 int main() {
159     srand((unsigned)time(0));
160     int count = 20, i;
161 
162     printf("### Function Test ###\n");
163 
164     printf("=== Init Skip List ===\n");
165     skiplist * sl = slCreate();
166     for ( i = 0; i < count; i++) {
167         slInsert(sl,i);
168     }
169     printf("=== Print Skip List ===\n");
170     slPrint(sl);
171 
172     printf("=== Search Skip List ===\n");
173     for (i = 0; i < count; i++) {
174         int value = rand()%(count+10);
175         slSearch(sl, value);
176     }
177     printf("=== Delete Skip List ===\n");
178     for (i = 0; i < count+10; i+=2) {
179         printf("Delete[%d]: %s\n", i, slDelete(sl, i)?"SUCCESS":"NOT FOUND");
180     }
181     slPrint(sl);
182 
183     slFree(sl);
184     sl = NULL;
185 }
186 #endif
复制代码

测试结果如下所示:

复制代码
 1 ### Function Test ###
 2 === Init Skip List ===
 3 === Print Skip List ===
 4 LEVEL[0]: 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10 -> 11 -> 12 -> 13 -> 14 -> 15 -> 16 -> 17 -> 18 -> 19 -> NULL
 5 LEVEL[1]: 0 -> 2 -> 4 -> 7 -> 9 -> 10 -> 11 -> 12 -> 14 -> 15 -> 17 -> 18 -> NULL
 6 LEVEL[2]: 7 -> 10 -> 12 -> 14 -> 15 -> NULL
 7 LEVEL[3]: 10 -> 14 -> 15 -> NULL
 8 LEVEL[4]: 10 -> 14 -> NULL
 9 LEVEL[5]: NULL
10 LEVEL[6]: NULL
11 LEVEL[7]: NULL
12 === Search Skip List ===
13 Found 1
14 Found 18
15 Not found 21
16 Not found 24
17 Found 10
18 Not found 20
19 Found 14
20 Found 10
21 Found 19
22 Found 18
23 Not found 27
24 Found 5
25 Found 0
26 Found 0
27 Found 18
28 Not found 26
29 Found 13
30 Not found 28
31 Not found 29
32 Not found 23
33 === Delete Skip List ===
34 Delete[0]: SUCCESS
35 Delete[2]: SUCCESS
36 Delete[4]: SUCCESS
37 Delete[6]: SUCCESS
38 Delete[8]: SUCCESS
39 Delete[10]: SUCCESS
40 Delete[12]: SUCCESS
41 Delete[14]: SUCCESS
42 Delete[16]: SUCCESS
43 Delete[18]: SUCCESS
44 Delete[20]: NOT FOUND
45 Delete[22]: NOT FOUND
46 Delete[24]: NOT FOUND
47 Delete[26]: NOT FOUND
48 Delete[28]: NOT FOUND
49 LEVEL[0]: 1 -> 3 -> 5 -> 7 -> 9 -> 11 -> 13 -> 15 -> 17 -> 19 -> NULL
50 LEVEL[1]: 7 -> 9 -> 11 -> 15 -> 17 -> NULL
51 LEVEL[2]: 7 -> 15 -> NULL
52 LEVEL[3]: 15 -> NULL
53 LEVEL[4]: NULL
54 LEVEL[5]: NULL
55 LEVEL[6]: NULL
56 LEVEL[7]: NULL

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值