跳躍表(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)
}

附錄

參考文檔

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