skiplist 原理介绍
skiplist 由William Pugh 在论文Skip Lists: A Probabilistic Alternative to Balanced Trees 中提出的一种数据结构,skiplist 是一种随机化存储的多层线性链表结构,插入,查找,删除的都是对数级别的时间复杂度。skiplist 和平衡树有相同的时间复杂度,但相比平衡树,skip实现起来更简单。
下图是wikipedia 上一个一个高度为4的skiplist
从垂直角度看,skiplist 的第0层以单链表的形式按照从小到大的顺序存储全部数据,越高层的链表的节点数越少,这样的特点实现了skiplist 在定位某个位置时,通过在高层较少的节点中查找就可以确定需要定位的位置处于哪个区间,从高层到低层不断缩小查找区间。以上图为例,比如我们需要在skiplist中查找2,查找过程如下,首先在最高层确定到2只可能处于1->NULL 这个区间,然后在第三层查找确定 2 只可能处于 1->4 这个区间,继续在第二层查找确定2 只可能处于1-3 这区间,最后在最底层1->3 这个区间查找可以确定2 是否存在于skiplist之中。
下图是wikipedia上提供的表示skiplist插入过程的一张gif,此图形象的说明了skiplist 定位以及插入节点的过程。
从水平角度来看,skiplist实现在链表开始的时候设置名为head 的哨兵节点,每一层链表的结束为止全部指向NULL。
leveldb 实现
leveldb 实现的skiplist位于db/skiplist.h。
skiplist Node 类型定义
1  | // Implementation details follow  | 
skiplist 类成员变量
1  | private:  | 
skiplist 插入
1  | template<typename Key, class Comparator>  | 
skiplist 查找
1  | template<typename Key, class Comparator>  | 
FindGreaterOrEqual
函数的作用是找到第一个大于或等于指定的key 的node,以及该node的前一个node1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27template<typename Key, class Comparator>
typename SkipList<Key,Comparator>::Node* SkipList<Key,Comparator>::FindGreaterOrEqual(const Key& key, Node** prev)
    const {
  Node* x = head_;
  // level 从0 开始编码
  int level = GetMaxHeight() - 1;
  while (true) {
	// 定位到当前level的下一个节点
    Node* next = x->Next(level);
    // key 没有在当前区间
    if (KeyIsAfterNode(key, next)) {
      // Keep searching in this list
      x = next;
    } else {
	  // key 在当前区间,在低level 继续查找,
	  // 在查找的同时设置prev 节点
      if (prev != NULL) prev[level] = x;
      // 在最低level找到相应位置
      if (level == 0) {
        return next;
      } else {
        // Switch to next list
        level--;
      }
    }
  }
}
RandomHeight
利用随机数实现每次有4分之一的概率增长高度。1
2
3
4
5
6
7
8
9
10
11
12template<typename Key, class Comparator>
int SkipList<Key,Comparator>::RandomHeight() {
  // Increase height with probability 1 in kBranching
  static const unsigned int kBranching = 4;
  int height = 1;
  while (height < kMaxHeight && ((rnd_.Next() % kBranching) == 0)) {
    height++;
  }
  assert(height > 0);
  assert(height <= kMaxHeight);
  return height;
}
FindLessThan
1  | template<typename Key, class Comparator>  | 
总结
skiplist最底层单链表有序存储全部元素,利用多层有序链表的结构实现加速索引的功能,处于越高level 节点的链表越稀疏查找速度越快,在不断向下查找的过程中不断缩小查找空间。
总的来说,skiplist 是一种设计巧妙的数据结构,leveldb 的实现可读性高,容易理解。