SkipList(Golang实现)

360 阅读2分钟

背景

SkipList在很多场景下都有用到,比如Redis SortSet、LSM Tree的memtable实现等。 SkipList实际上是一种可以进行二分查找的有序链表。插入、搜索、删除平均时间复杂度为O(logN)

与其他数据结构的对比

图片GoLang实现跳跃表主要的功能

  1. 检索(search)
  2. 新增(insert)
  3. 删除(delete)
  4. 遍历 实际上skiplist paper已经给出了检索、新增、删除 实现的伪代码

具体实现

const (  
    MaxLevel int  = 16      // 可以容纳的大小为 2 ^ 16  
    Probability float32 = 0.25    //  1/4
)

主要数据结构

// 跳表
type SkipList struct {  
    Level int           // 跳跃表的层数  
    Len   int           // 当前元素的个数   
    Head  *Element      // 哑节点  
    Mutex sync.RWMutex  // 读写锁
}
// 元素type 
Element struct {  
    Forward []*Element  // 存储向前的指针列表   
    Key     int         // 元素的key   
    Value   interface{} // 元素的value
 }

检索

跳表的数据结构实际上不太好理解的是Foward []*Element,实际上数组的下标表示的是当前跳表指针所处的层数,其他就和链表是类似的。

       图片

实际上不是SkipList不是像图这样遍历的,图只是简化了流程 比如我想检索SkipList  =>  Key为12的数据

  1. 此时指针处于第四层,首先到key = 6  因为x.Forward[i] == nil 跳出子循环,到下一层。
  2. 此时指针处于第三层 ,  key = 25  , 25 > 12 跳出子循环到下一层。
  3. 此时指针处于第二层 ,  key = 9  ,  9 <  12 ,指针移动到9所处位置 , 17 > 12 退出子循环到下一层
  4. 此时指针处于第一层.  key = 12   , 12 < 12 此时因为到了最后一层,退出全部循环。
  5. 判断第0层指针指向的key与所要找的key是否相等
func (s *SkipList) Search(key int) (element *Element, ok bool) {  
    x := s.Head  
    // 1.从最高层开始搜索  
    for i := s.Level - 1; i >= 0; i-- {    
    // 2.看这一层的元素是否有小于这个key的    
    for x.Forward[i] != nil && x.Forward[i].Key < key {     
        x = x.Forward[i]   
    }    
    // 3.如果没有就遍历下一层  
    }  
    x = x.Forward[0] 
    if x != nil && x.Key == key {    
        return x, true  
    }  
        return nil, false
 }

插入操作

   图片

当我们向SkipList插入一个元素的时候,我们可能会思考,将这个元素插入到第几层合适呢?实际上插入的层数是由P(概率)来控制的,这个值一般是1/2或者是1/4。      图片 图片 插入操作相对查询和删除相对复杂,还是举例来看,比如我想插入17

  1. 指针处于第四层,首先还是从哑结点开始遍历, 第四层指针在6节点之后。
  2. 指针处于第三层,第三层加在6节点之后。
  3. 指针处于第二层,第二层加在9节点之后。
  4. 指针处于第一层,第一层加在12节点之后。

// 插入一个新的元素
func (s *SkipList) Insert(key int, value interface{}) *Element {
  // 1.加读写锁
  s.Mutex.RLock()
  defer s.Mutex.RUnlock()
  // 创建一个新的element
  update := make([]*Element, MaxLevel)
  x := s.Head
  for i := s.Level - 1; i >= 0; i-- {
    for x.Forward[i] != nil && x.Forward[i].Key < key {
      x = x.Forward[i]
    }
    update[i] = x
  }
  x = x.Forward[0]
  // 如果当前x!=nil && x.key = key
  if x != nil && x.Key == key {
    x.Value = value
    return x
  }
  // 随机生成一个level
  level := randomLevel()
  // 如果生成的level大于当前level
  if level > s.Level {
    level = s.Level + 1
    update[s.Level] = s.Head
    s.Level = level
  }
  // 创建一个元素,指定level的层数
  e := newElement(key, value, level)
  for i := 0; i < level; i++ {
    e.Forward[i] = update[i].Forward[i]
    update[i].Forward[i] = e
  }
  s.Len++
  return e
}

删除操作

删除操作相对于插入操作实现更简单。

func (s *SkipList) Delete(key int) *Element { 
    s.Mutex.RLock()   
    defer s.Mutex.RUnlock()   
    update := make([]*Element, MaxLevel)   
    x := s.Head   
    for i := s.Level - 1; i >= 0; i-- {     
        for x.Forward[i] != nil && x.Forward[i].Key < key { 
            x = x.Forward[i]     
            } 
            update[i] = x  
     }
     x = x.Forward[0]   
     if x != nil && x.Key == key {  
          for i := 0; i < s.Level; i++ {      
             if update[i].Forward[i] != x {   
             return nil       
           }             
             update[i].Forward[i] = x.Forward[i]   
          }     
                s.Len--  
            }   
                return x
     }

遍历SkipList

比如我向skipList插入1~18 ,skiplist可能如下图所示。       图片

func (s *SkipList) DumpSkipList() {
   x := s.Head
   // 1.从最高层开始搜索
   for i := s.Level - 1; i >= 0; i-- {
      x = s.Head
      // 2.看这一层的元素是否有小于这个key的
      for x.Forward[i] != nil {
         if x.Forward[i].Forward[i] == nil {
            fmt.Print(x.Forward[i].Key)
         } else {
            fmt.Print(x.Forward[i].Key, "-->")
         }
         x = x.Forward[i]
      }
      fmt.Println("")
   }
}

Reference