1957

无聊蛋疼的1957写的低端博客
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

skiplist poj2892

Posted on 2012-10-09 16:11  1957  阅读(672)  评论(0编辑  收藏  举报

在微博上看到了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有空慢慢改。。。