http://www.spongeliu.com/63.html
http://imtinx.iteye.com/blog/1291165
什么是跳表?要说清楚这个问题,我们就要先从普通的有序链表说起。一个普通有序列表的结构如下:
![]()
我们可以看到,上图所示的链表按照由小到大的顺序排列(-1表示最小值,1表示最大值,这是本文的一个约定),如果我们想要查找一个元素x,算法如下:
上面这个算法得到了x元素的前驱或者所有大于x的元素中最小的一个元素。
基于上面这个链表,我们想要插入一个元素35的算法是:
想要删除元素37的算法是:
我想这些算法大家都应该是耳熟能详了,对于这样一个链表,查找、删除、插入一个元素的时间复杂度都是O(n)。
*********************我是分割线************************
好,下面我们正式开始介绍跳表。跳表是个概率性数据结构,可以被看作是二叉树的一个变种。跳表是由William Pugh在1990年发明的。它是一种用户维护有序元素的数据结构。
跳表的构造过程是:
1、给定一个有序的链表。
2、选择连表中最大和最小的元素,然后从其他元素中按照一定算法随即选出一些元素,将这些元素组成有序链表。这个新的链表称为一层,原链表称为其下一层。
3、为刚选出的每个元素添加一个指针域,这个指针指向下一层中值同自己相等的元素。Top指针指向该层首元素
4、重复2、3步,直到不再能选择出除最大最小元素以外的元素。
一个跳表,应该具有以下特征:
- 一个跳表应该有几个层(level)组成;
- 跳表的第一层包含所有的元素;
- 每一层都是一个有序的链表;
- 如果元素x出现在第i层,则所有比i小的层都包含x;
- 第i层的元素通过一个down指针指向下一层拥有相同值的元素;
- 在每一层中,-1和1两个元素都出现(分别表示INT_MIN和INT_MAX);
- Top指针指向最高层的第一个元素。
让我们用一个跳表来重新构建文章开头的有序链表:

通过图我们可以看出,整个跳表分为三层,每一层都是一个有序链表,第一层包含所有的元素。top指针指向最高层的-1元素,较高层的元素都能在较低的层里找到,并且较高层的元素含有一个指针指向下一层值相同的元素。
上面的特征和图基本就给出了一个跳表的定义和结构。至于哪些元素应该再更高一层中保留,我们会在下面叙述。
在结构清晰之后,我们需要明白的是跳表为什么要这样设计?这么存储的好处是什么呢?让我们通过对跳表操作来寻找答案。
首先是查找操作。在跳表中查找一个元素x的算法如下:
接着是插入算法。假设要插入一个元素“119”,我们设定需要插入该元素的层数为“k”(即我们需要在所有的[1,k]范围内的层里都插入元素。k的值我们会在下文中叙述),则插入算法是:
最后是删除操作。删除一个元素x,如果x被删除后某层只剩下头尾两个节点,则删除这一层。具体算法如下:
好了,我们可以看到,无论查找、插入、删除,跳表的时间复杂度都是O(lgn)!这就是为什么我们要引入跳表。
最后,让我们来阐述哪些元素应该在上一层保留,以及插入操作时确定插入元素的层数k。
哪些元素应该在高层保留,是随机决定的。具体算法如下:
- 我们假定一个函数rand(),随机返回1或者0
- 假设元素i最多在第k层保留
- k的值由程式“ while(rand()) k++;”来决定
看明白了么?也就是从第一层随机选出一些元素放到第二层,再从第二层随机选出元素放到第三层,以此类推,知道没有元素再被选出。插入操作时被插入元素的层数也是这么得来的。
////////////////////////////////////////
丢硬币决定 K
插入元素的时候,元素所占有的层数完全是随机的,通过一下随机算法产生:
- int random_level()
- {
- K = 1;
- while (random(0,1))
- K++;
- return K;
- }
相当与做一次丢硬币的实验,如果遇到正面,继续丢,遇到反面,则停止,
用实验中丢硬币的次数 K 作为元素占有的层数。显然随机变量 K 满足参数为 p = 1/2 的几何分布,
K 的期望值 E[K] = 1/p = 2. 就是说,各个元素的层数,期望值是 2 层。
这个代码不错
http://blog.youkuaiyun.com/likun_tech/article/details/7354306
#include <stdio.h> #include <stdlib.h> #include <malloc.h> typedef int key_t; typedef int value_t; typedef struct node_t { key_t key; value_t value; struct node_t *forward[]; } node_t; typedef struct skiplist { int level; int length; node_t *header; } list_t; #define MAX_LEVEL 16 #define SKIPLIST_P 0.25 node_t* slCreateNode(int level, key_t key, value_t value) { node_t *n = (node_t *)malloc(sizeof(node_t) + level * sizeof(node_t*)); if(n == NULL) return NULL; n->key = key; n->value = value; return n; } list_t* slCreate(void) { list_t *l = (list_t *)malloc(sizeof(list_t)); int i = 0; if(l == NULL) return NULL; l->length = 0; l->level = 0; l->header = slCreateNode(MAX_LEVEL - 1, 0, 0); for(i = 0; i < MAX_LEVEL; i++) { l->header->forward[i] = NULL; } return l; } int randomLevel(void) { int level = 1; while ((rand()&0xFFFF) < (SKIPLIST_P * 0xFFFF)) level += 1; return (level<MAX_LEVEL) ? level : MAX_LEVEL; } value_t* slSearch(list_t *list, key_t key) { node_t *p = list->header; int i; for(i = list->level - 1; i >= 0; i--) { while(p->forward[i] && (p->forward[i]->key <= key)){ if(p->forward[i]->key == key){ return &p->forward[i]->value; } p = p->forward[i]; } } return NULL; } int slDelete(list_t *list, key_t key) { node_t *update[MAX_LEVEL]; node_t *p = list->header; node_t *last = NULL; int i = 0; for(i = list->level - 1; i >= 0; i--){ while((last = p->forward[i]) && (last->key < key)){ p = last; } update[i] = p; } if(last && last->key == key){ for(i = 0; i < list->level; i++){ if(update[i]->forward[i] == last){ update[i]->forward[i] = last->forward[i]; } } free(last); for(i = list->level - 1; i >= 0; i--){ if(list->header->forward[i] == NULL){ list->level--; } } list->length--; }else{ return -1; } return 0; } int slInsert(list_t *list, key_t key, value_t value) { node_t *update[MAX_LEVEL]; node_t *p, *node = NULL; int level, i; p = list->header; for(i = list->level - 1; i >= 0; i--){ while((node = p->forward[i]) && (node->key < key)){ p = node; } update[i] = p; } if(node && node->key == key){//不插入相同的元素 node->value = value; return 0; } level = randomLevel(); if (level > list->level)//如果要插入的总的层数大于现有的层数 { for(i = list->level; i < level; i++){ update[i] = list->header; } list->level = level; } node = slCreateNode(level, key, value); for(i = 0; i < level; i++){ node->forward[i] = update[i]->forward[i]; update[i]->forward[i] = node; } list->length++; return 0; } int main(int argc, char **argv) { list_t *list = slCreate(); node_t *p = NULL; value_t *val = NULL; //插入 for(int i = 1; i <= 15; i++){ slInsert(list, i, i*10); } //删除 if(slDelete(list, 12) == -1){ printf("delete:not found\n"); }else{ printf("delete:delete success\n"); } //查找 val = slSearch(list, 1); if(val == NULL){ printf("search:not found\n"); }else{ printf("search:%d\n", *val); } //遍历 p = list->header->forward[0]; for(int i = 0; i < list->length; i++){ printf("%d,%d\n", p->key, p->value); p = p->forward[0]; } getchar(); return 0; }
本文介绍了跳表的概念、结构和操作方法,重点解释了查找、插入和删除算法,以及为何跳表的时间复杂度为O(lgn),并详细阐述了随机选择元素和确定插入层数的算法。


1735

被折叠的 条评论
为什么被折叠?



