SkipList跳跃表

来源:互联网 发布:91家居设计软件 编辑:程序博客网 时间:2024/05/16 11:45

转载 :http://blog.csdn.net/duan19920101/article/details/51579136
在学习rocksdb 时,组件memtable 存储数据的主要数据结构是skiplist,不是太了解,学习后感觉设计很精巧。

1.什么是SkipList跳跃表

跳表是平衡树的一种替代的数据结构,但是和红黑树不相同的是,跳表对于树的平衡的实现是基于一种随机化的算法的,这样也就是说跳表的插入和删除的工作是比较简单的。

2.核心思想

先从链表开始,如果是一个简单的链表,那么我们知道在链表中查找一个元素I的话,需要将整个链表遍历一次。
这里写图片描述

如果是说链表是排序的,并且节点中还存储了指向前面第二个节点的指针的话,那么在查找一个节点时,仅仅需要遍历N/2个节点即可。

这里写图片描述

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

3.相关操作

1、重要数据结构定义

2、初始化表

3、查找

4、插入

5、删除

6、释放表

7、性能比较

1.结构定义:

//定义key和value的类型  typedef int KeyType;  typedef int ValueType;  //定义结点  typedef struct nodeStructure* Node;  struct nodeStructure{      KeyType key;      ValueType value;      Node forward[1];  };  //定义跳跃表  typedef struct listStructure* List;  struct listStructure{      int level;      Node header;  }; 

2.初始化跳表

    void SkipList::NewList(){          //设置NIL结点          NewNodeWithLevel(0, NIL_);          NIL_->key = 0x7fffffff;          //设置链表List          list_ = (List)malloc(sizeof(listStructure));          list_->level = 0;          //设置头结点          NewNodeWithLevel(MAX_LEVEL,list_->header);          for(int i = 0; i < MAX_LEVEL; ++i){              list_->header->forward[i] = NIL_;          }          //设置链表元素的数目          size_ = 0;      }      void SkipList::NewNodeWithLevel(const int& level,                                      Node& node){          //新结点空间大小          int total_size = sizeof(nodeStructure) + level*sizeof(Node);          //申请空间          node = (Node)malloc(total_size);          assert(node != NULL);      }  

3.查找

我们从头结点开始,首先和9进行判断,此时大于9,然后和21进行判断,小于21,此时这个值肯定在9结点和21结点之间,此时,我们和17进行判断,大于17,然后和21进行判断,小于21,此时肯定在17结点和21结点之间,此时和19进行判断,找到了
这里写图片描述

    bool SkipList::Search(const KeyType& key,                            ValueType& value){          Node x = list_->header;          int i;          for(i = list_->level; i >= 0; --i){              while(x->forward[i]->key < key){                  x = x->forward[i];              }          }          x = x->forward[0];          if(x->key == key){              value = x->value;              return true;          }else{              return false;          }      }  

4.插入

插入包含如下几个操作:1、查找到需要插入的位置   2、申请新的结点    3、调整指针。

这里写图片描述

      bool SkipList::Insert(const KeyType& key,                            const ValueType& value){          Node update[MAX_LEVEL];          int i;          Node x = list_->header;          //寻找key所要插入的位置          //保存大于key的位置信息          for(i = list_->level; i >= 0; --i){              while(x->forward[i]->key < key){                  x = x->forward[i];              }              update[i] = x;          }          x = x->forward[0];          //如果key已经存在          if(x->key == key){              x->value = value;              return false;          }else{              //随机生成新结点的层数              int level = RandomLevel();              //为了节省空间,采用比当前最大层数加1的策略              if(level > list_->level){                  level = ++list_->level;                  update[level] = list_->header;              }              //申请新的结点              Node newNode;              NewNodeWithLevel(level, newNode);              newNode->key = key;              newNode->value = value;              //调整forward指针              for(int i = level; i >= 0; --i){                  x = update[i];                  newNode->forward[i] = x->forward[i];                  x->forward[i] = newNode;              }              //更新元素数目              ++size_;              return true;          }      }    

5.删除

删除需要3步:1、查找到需要删除的结点 2、删除结点 3、调整指针

这里写图片描述

    bool SkipList::Delete(const KeyType& key,                            ValueType& value){          Node update[MAX_LEVEL];          int i;          Node x = list_->header;          //寻找要删除的结点          for(i = list_->level; i >= 0; --i){              while(x->forward[i]->key < key){                  x = x->forward[i];              }              update[i] = x;          }          x = x->forward[0];          //结点不存在          if(x->key != key){              return false;          }else{              value = x->value;              //调整指针              for(i = 0; i <= list_->level; ++i){                  if(update[i]->forward[i] != x)                      break;                  update[i]->forward[i] = x->forward[i];              }              //删除结点              free(x);              //更新level的值,有可能会变化,造成空间的浪费              while(list_->level > 0                  && list_->header->forward[list_->level] == NIL_){                  --list_->level;              }              //更新链表元素数目              --size_;              return true;          }      }  

6.释放表

这里写图片描述

    void SkipList::FreeList(){          Node p = list_->header;          Node q;          while(p != NIL_){              q = p->forward[0];              free(p);              p = q;          }          free(p);          free(list_);      }  

7.性能比较

这里写图片描述

通过“空间来换取时间”的一个算法,通过在每个节点中增加了向前的指针,从而提升查找的效率。