跳跃表(Skip Links)实现(Golang版)

目录

跳跃表(Skip Links)实现(Golang版)

1. 介绍

2. 跳跃表在Redis中的应用

3. 跳跃表实现(Golang版)

3.1 实现介绍

3.2 实现源码

附录

参考文档


跳跃表(Skip Links)实现(Golang版)

1. 介绍

跳跃表(Skip Lists)是一种随机化的数据, 由 William Pugh 在论文《Skip lists: a probabilistic alternative to balanced trees》中提出, 跳跃表以有序的方式在层次化的链表中保存元素, 效率和平衡树媲美 —— 查找、删除、添加等操作都可以在对数期望时间下完成, 并且比起平衡树来说, 跳跃表的实现要简单直观得多。

本文注重跳跃表的实现,关于跳跃表的原理介绍,网上有很多优质的介绍文档,这里就不再介绍跳跃表的实现原理了。

2. 跳跃表在Redis中的应用

Redis的有序集合(sorted set)是基于跳跃表实现的,相比 William Pugh 论文中描述的跳跃表,Redis做了一些修改,其中包括:

  1. 允许重复的 score 值:多个不同的 member 的 score 值可以相同。
  2. 进行对比操作时,不仅要检查 score 值,还要检查 member :当 score 值可以重复时,单靠 score 值无法判断一个元素的身份,所以需要连 member 域都一并检查才行。
  3. 每个节点都带有一个高度为 1 层的后退指针,用于从表尾方向向表头方向迭代:当执行ZREVRANGEZREVRANGEBYSCORE这类以逆序处理有序集的命令时,就会用到这个属性。

以下展示了Redis有序集合(sorted set)的一个可能结构:


注意:图中前向指针上面括号中的数字,表示对应的span的值。即当前指针跨越了多少个节点,这个计数不包括指针的起点节点,但包括指针的终点节点。
图取于此:http://zhangtielei.com/posts/blog-redis-skiplist.html
 


3. 跳跃表实现(Golang版)

3.1 实现介绍

  • 参考Redis有序集合(sorted set)设计原理与实现源码。
  • 只实现三个基础接口:Search(查询)、Add(添加)、Erase(删除)
  • 支持设置最大层级数(Determining MaxLevel)和上升索引概率(Probabilistic Philosophy)。
  • 提供一个Println接口,用于调试测试。

3.2 实现源码

package skiplinks

import (
	"fmt"
	"math/rand"
	"strings"
	"time"
)

const (
	defaultSkipLinkedP = 0.5
	defaultMaxLevel    = 64
)

type SkipLinks struct {
	// key和score的映射表
	scoreMap map[string]float64
	// 当前层级数
	curLevel uint16
	// 最大层级数
	maxLevel uint16
	// 最小值
	minScore float64
	// 上升索引概率
	skipLinkedP float64
	// 头节点
	head *Node
	// 随机参数
	rand *rand.Rand
}

func NewSkipLinked(maxLevel uint16, minScore float64, p ...float64) *SkipLinks {
	skipLinkedP := defaultSkipLinkedP
	if len(p) > 0 && float64(0) <= p[0] && p[0] < float64(1) {
		skipLinkedP = p[0]
	}
	if maxLevel <= 0 || maxLevel > maxLevel {
		maxLevel = defaultMaxLevel
	}
	return &SkipLinks{
		scoreMap:    make(map[string]float64),
		curLevel:    1,
		maxLevel:    maxLevel,
		minScore:    minScore,
		skipLinkedP: skipLinkedP,
		head:        NewNode("", minScore, nil, maxLevel),
		rand:        rand.New(rand.NewSource(time.Now().UnixNano())),
	}
}

func (l *SkipLinks) getLevel() uint16 {
	level := uint16(1)
	for l.rand.Float64() < l.skipLinkedP && level < l.maxLevel {
		level++
	}
	return level
}

func (l *SkipLinks) compare(score1, score2 float64, key1, key2 string) int {
	if score1 > score2 {
		return 1
	} else if score1 < score2 {
		return -1
	} else {
		return strings.Compare(key1, key2)
	}
}

func (l *SkipLinks) Search(key string) (bool, interface{}) {
	score, exist := l.scoreMap[key]
	if !exist {
		return false, nil
	}
	exist, node := l.search(key, score, l.head, l.curLevel)
	if !exist {
		return false, nil
	}
	return true, node.Val
}

func (l *SkipLinks) search(key string, score float64, node *Node, level uint16) (bool, *Node) {
	next := node.Next[level-1]
	for next != nil && l.compare(next.Score, score, next.Key, key) <= 0 {
		node = node.Next[level-1]
		next = node.Next[level-1]
	}
	if l.compare(node.Score, score, node.Key, key) == 0 {
		return true, node
	}
	if level == 1 {
		return false, node
	}
	return l.search(key, score, node, level-1)
}

func (l *SkipLinks) Add(key string, score float64, val interface{}) error {
	if score < l.minScore {
		return NewScoreOutOfRangeError(key, score, l.minScore)
	}
	if key == "" {
		return NewInvalidKeyError(key, "empty key")
	}
	_, exist := l.scoreMap[key]
	if exist {
		l.Erase(key)
	}
	high, newNode := l.add(key, score, val, l.head, l.curLevel)
	if high > l.curLevel {
		// Add之后,层数可能加一
		l.head.Next[l.curLevel] = newNode
		l.curLevel++
	}
	l.scoreMap[key] = score
	return nil
}

func (l *SkipLinks) add(key string, score float64, val interface{}, node *Node, level uint16) (uint16, *Node) {
	next := node.Next[level-1]
	for next != nil && l.compare(next.Score, score, next.Key, key) <= 0 {
		node = node.Next[level-1]
		next = node.Next[level-1]
	}
	if level == 1 {
		newNode := NewNode(key, score, val, l.maxLevel)
		newNode.Next[level-1] = next
		node.Next[level-1] = newNode
		return l.getLevel(), newNode
	}
	high, newNode := l.add(key, score, val, node, level-1)
	if high >= level {
		newNode.Next[level-1] = next
		node.Next[level-1] = newNode
	}
	return high, newNode
}

func (l *SkipLinks) Erase(key string) (bool, interface{}) {
	score, exist := l.scoreMap[key]
	if !exist {
		return false, nil
	}
	delete(l.scoreMap, key)
	exist, node := l.erase(key, score, l.head, l.curLevel)
	if !exist {
		return false, nil
	}
	for i := int(l.curLevel - 1); i > 0; i-- {
		if l.head.Next[i] == nil {
			// Erase之后,层数可能减少
			l.curLevel--
		} else {
			break
		}
	}
	return true, node.Val
}

func (l *SkipLinks) erase(key string, score float64, node *Node, level uint16) (bool, *Node) {
	next := node.Next[level-1]
	for next != nil && l.compare(next.Score, score, next.Key, key) < 0 {
		node = next
		next = next.Next[level-1]
	}
	if level == 1 {
		if next != nil && l.compare(next.Score, score, next.Key, key) == 0 {
			node.Next[level-1] = next.Next[level-1]
			next.Next[level-1] = nil
			return true, next
		} else {
			return false, nil
		}
	}
	exist, rmNode := l.erase(key, score, node, level-1)
	if exist && next != nil && l.compare(next.Score, score, next.Key, key) == 0 {
		node.Next[level-1] = next.Next[level-1]
		next.Next[level-1] = nil
	}
	return exist, rmNode
}

func (l *SkipLinks) Println() {
	for i := int(l.curLevel - 1); i >= 0; i-- {
		var (
			content string = fmt.Sprintf("level%d", i)
			node    *Node  = l.head.Next[i]
		)
		for node != nil {
			content += fmt.Sprintf(" %s(%v)", node.Key, node.Score)
			node = node.Next[i]
		}
		fmt.Println(content)
	}
}

type Node struct {
	Key   string
	Score float64
	Val   interface{}
	Next  []*Node
}

func NewNode(key string, score float64, val interface{}, maxLevel uint16) *Node {
	return &Node{
		Key:   key,
		Score: score,
		Val:   val,
		Next:  make([]*Node, maxLevel, maxLevel),
	}
}

type ScoreOutOfRangeError struct {
	key   string
	score float64
	min   float64
}

func NewScoreOutOfRangeError(key string, score float64, min float64) *ScoreOutOfRangeError {
	return &ScoreOutOfRangeError{
		key:   key,
		score: score,
		min:   min,
	}
}

func (e *ScoreOutOfRangeError) Error() string {
	return fmt.Sprintf("score out of range, min(include)=%v, score=%v, key=%s", e.min, e.score, e.key)
}

type InvalidKeyError struct {
	key    string
	reason string
}

func NewInvalidKeyError(key, reason string) *InvalidKeyError {
	return &InvalidKeyError{
		key:    key,
		reason: reason,
	}
}

func (e *InvalidKeyError) Error() string {
	return fmt.Sprintf("invalid key: %s, reason: %s", e.key, e.reason)
}

附录

参考文档

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章