背景
SkipList在很多场景下都有用到,比如Redis SortSet、LSM Tree的memtable实现等。 SkipList实际上是一种可以进行二分查找的有序链表。插入、搜索、删除平均时间复杂度为O(logN)
与其他数据结构的对比
GoLang实现跳跃表主要的功能
- 检索(search)
- 新增(insert)
- 删除(delete)
- 遍历 实际上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的数据
- 此时指针处于第四层,首先到key = 6 因为x.Forward[i] == nil 跳出子循环,到下一层。
- 此时指针处于第三层 , key = 25 , 25 > 12 跳出子循环到下一层。
- 此时指针处于第二层 , key = 9 , 9 < 12 ,指针移动到9所处位置 , 17 > 12 退出子循环到下一层
- 此时指针处于第一层. key = 12 , 12 < 12 此时因为到了最后一层,退出全部循环。
- 判断第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
- 指针处于第四层,首先还是从哑结点开始遍历, 第四层指针在6节点之后。
- 指针处于第三层,第三层加在6节点之后。
- 指针处于第二层,第二层加在9节点之后。
- 指针处于第一层,第一层加在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("")
}
}
- 具体代码可以参考 github.com/WenRuige/ea…