在微博上看到了skiplist,于是就想自己实现下下。
关于skiplist的时间复杂度的证明还没看懂,有兴趣的可以去看看 Skip Lists:A ProbabilisticAlternative toBalanced Trees 这篇论文。
ok,这里就只说实现了。
其实论文里面的伪代码很清楚了,照着写就行了。
skiplist就不多说了http://en.wikipedia.org/wiki/Skiplist
还是使用我们喜闻乐见的c语言实现吧
首先要的就是定义每个节点的数据类型啦。
typedef struct snode { int key; int level; int value; struct snode* forward[maxlevel]; }Node;
每个节点有自己的level和forward指针,其实似乎level在每个节点定义木有啥用的哈- -,囧。。。
然后定义skiplist的结构
typedef struct slist { struct snode* head; int level; }skiplist;
我看别人的代码里面有很多东西,但是我现在只是实现基本功能所以也就这两个就够了。
然后是一些基本的makeNode , randomLevel等
Node* makeNode(int newLevel , int key , int value) { Node* tmp = (Node *)malloc(sizeof(Node)); tmp -> key = key; tmp -> value = value; tmp -> level = newLevel; return tmp; } int randomLevel() { int newLevel = 1; while((rand()&0xFFFF) < (0.4 * 0xFFFF)) newLevel ++; return newLevel < maxlevel ? newLevel : maxlevel; }
注意就是randomLevel,其实就是一个概率p,当rand < p的时候newLevel+1
但是c语言似乎木有产生[0..1]随机数的函数,只有用rand()了
用&0xFFFF把数字截断 , 然后p*0xFFFF来实现,不过感觉为啥p=0.5就死循环了,这个还没搞明白。
void Init(skiplist * list) { list -> head = (Node*)malloc(sizeof(Node)); for(int i = 0 ; i < maxlevel ; i++) list -> head -> forward[i] = NULL; list -> level = 0; }
初始化skiplist
好,关键的Insert , Delete来了
void Insert(skiplist* list , int key , int value) { Node* update[maxlevel]; Node* x = list -> head; for(int i = list -> level - 1 ; i >= 0 ; i--) { while(x -> forward[i] != NULL && x -> forward[i]->key < key) x = x -> forward[i]; update[i] = x; } x = x -> forward[0]; if(x != NULL && x -> key == key) x -> value = value; else{ int newLevel = randomLevel(); // cout<< "level = " <<newLevel<<endl; if (newLevel > list -> level) { for(int i = list -> level ; i < newLevel ; i++) update[i] = list -> head; list -> level = newLevel; } x = makeNode(newLevel , key , value); for(int i = 0 ; i < newLevel ; i++) { x -> forward[i] = update[i] -> forward[i]; update[i] -> forward[i] = x; } } } void Delete(skiplist* list , int key) { Node* update[maxlevel]; Node* x = list -> head; for(int i = list -> level - 1 ; i >=0 ; i--) { while(x -> forward[i] != NULL && x -> forward[i] -> key < key) x = x -> forward[i]; update[i] = x; } x = x -> forward[0]; if(x != NULL && x -> key == key) { for(int i = 0 ; i < x -> level ; i++) if(update[i] -> forward[i] != x) break; else update[i] -> forward[i] = x -> forward[i]; free(x); while(list -> level > 0 && list -> head -> forward[list -> level - 1] == NULL ) list -> level --; } }
一纬链表的插入,删除我们都懂,其实这个和那个一样的原理。
对于插入就是先找到位置,就是key处于左右两边数字的中间的时候。
然后新建节点,将前面的forward指向他,他的forward指向前面的forward。
关键就是用一个update数组来记录x前面指向 它的节点(lasted),也就是x得前面节点。
因为有多个forward,所以记录每个forward
Delete同理。
==========================无敌分割线============================
来做poj2892
那就是将删除的点Insert
如果rebuild那么久Delete
如果Q,那么久找这个前面最大,和后面最小的,就是lessthan , greaterthan的操作
因为在代码上改的,所以就没有把lessthan, greaterthan分别写成函数了
int Search(skiplist* list , int key) { Node* x = list -> head; for(int i = list -> level - 1 ; i >=0 ; i--) { while(x -> forward[i] != NULL && x -> forward[i]-> key < key) x = x -> forward[i]; } if(x -> forward[0] != NULL && x -> forward[0] -> key == key) { printf("0\n"); return x -> value; }else{ //right int right , left; if(x -> forward[0] == NULL) right = n ; else right = x -> forward[0] -> key - 1; if(x == list->head) left = 1 ; else left = x -> key + 1; // printf("left = %d , right = %d \n" , left , right); printf("%d\n",right - left + 1); return -1; } }
ok...但是我初次乱写实现的效率很低很低。。。
用g++交了还TLE,用C++交938MS。。差点超时。。。T——T有空慢慢改。。。