原文地址:https://mp.weixin.qq.com/s/9IJRJcRz5WiCUqxScYkVrQ
背景:
相信大家都知道排行榜,在很多场景里都需要用到排行榜功能,尤其是游戏中!之前在了解排行榜实现机制的时候,在网上看得最多的答复便是使用redis的有序集合实现。于是深入了解了一下redis中的有序集合。
redis中的有序结合(sorted set)是一种线性结构,底层是用有序链表实现的,但是对链表增加的“索引”,并且对索引进行了分层,跳表每层散落的节点数不同,查找过程中通过索引向下层跳转,最终落到最下层的数据链中,这样做的目的是通过空间换时间来获取更高的效率,故称之为跳跃表(跳表)。
1,跳表简介
跳表是由William Pugh发明的,这位确实是个大牛,搞出一些很不错的东西。简单说来跳表也是
链表的一种,只不过它在链表的基础上增加了跳跃功能,正是这个跳跃的功能,使得在查找元素时,跳表能够提供O(log n)的时间复杂
度。红黑树等这样的平衡数据结构查找的时间复杂度也是O(log n),并且相对于红黑树这样的平衡二叉树skiplist的优点是更好的支持并
发操作,但是要实现像红黑树这样的数据结构并非易事,但是只要你熟悉链表的基本操作,再加之对跳表原理的理解,实现一个跳表数据
结构就是一个很自然的事情了。
此外,跳表在当前热门的开源项目中也有很多应用,比如LevelDB的核心数据结构memtable是用跳表实现的,redis的sorted set数据
结构也是有跳表实现的。
2,skiplist主要思想
先从链表开始,如果是一个简单的链表(不一定有序),那么我们在链表中查找一个元素X的话,需要将遍历整个链表直到找到元素X为止。
有序数组查找问题我们可以使用二分查找算法,但对于有序链表却不能使用二分查找。这个时候我们在想下平衡树,比如BST,他们都是通过把一些
节点取出来作为其节点下某种意义的索引,比如父节点一般大于左子节点而小于右子节点。因此这个时候我们想到类似二叉搜索树的做法把一些
节点提取出来,作为索引。
当然我们还可以再从一级索引提取一些元素出来,作为二级索引,这样更能加快元素搜索。
这基本上就是跳表的核心思想,其实是一种通过“空间来换取时间”的一个算法,通过在每个节点中增加了向前的指针(即层),从而提升查找的效率。
跳跃列表是按层建造的。底层是一个普通的有序链表。每个更高层都充当下面列表的「快速跑道」,这里在层 i 中的元素按某个固定的概率 p (通常
为0.5或0.25)出现在层 i+1 中。平均起来,每个元素都在 1/(1-p) 个列表中出现, 而最高层的元素(通常是在跳跃列表前端的一个特殊的头元素)
在 O(log1/p n) 个列表中出现。
3,SkipList基本数据结构及其实现
一个跳表,应该具有以下特征:
1>,一个跳表应该有几个层(level)组成;
2>,跳表的第一层包含所有的元素;
3>,每一层都是一个有序的链表;
4>,如果元素x出现在第i层,则所有比i小的层都包含x;
5>,每个节点包含key及其对应的value和一个指向同一层链表的下个节点的指针数组
4,注意问题
1>,如果放任增长,高度失去控制会影响效果,所以一般会限制最高高度
2>,如果删除的值在索引中,注意要删除每一层
5.代码实现
https://github.com/pyihe/go-skipList
package main import ( "fmt" "math/rand" "time" ) const MAX_LEVEL=10 type Skiplist struct { maxLevel int root []*Node } type Node struct{ level int next []*Node prev []*Node value int count int } func NewNode(level,value int)*Node{ return &Node{ level:level, value:value, next:make([]*Node,level+1), prev:make([]*Node,level+1), count:1, } } func Constructor() Skiplist { return Skiplist{ maxLevel:-1, } } func (this *Skiplist) SearchPos(target int)*Node{ if this.maxLevel==-1{ return nil } next:=this.root[this.maxLevel] for level:=next.level;level>=0;level--{ if target==next.value{ return next }else if target<next.value{ for next.prev[level]!=nil && target<=next.prev[level].value{ next=next.prev[level] } }else{ for next.next[level]!=nil && target>=next.next[level].value{ next=next.next[level] } } fmt.Println("search :",target," level",level) } return next } func (this *Skiplist) Search(target int) bool { n:=this.SearchPos(target) this.Print() if n==nil{ return false } return n.value==target } func(this *Skiplist)Print(){ if this.root==nil{ return } fmt.Println(*this) for i:=this.maxLevel;i>=0;i--{ cur:=this.root[i] for cur!=nil && cur.next!=nil{ fmt.Print("->[",cur.value,cur.count,"]") cur=cur.next[i] } fmt.Println(i) } } func(this*Skiplist)randomUpgrade()bool{ rand.Seed(time.Now().UnixNano()) r:=rand.Intn(7)%2 if this.maxLevel>MAX_LEVEL{ return false } fmt.Println("rand:---",r) return r==1 } func(n*Node)InsertLevelNode(level int,nn *Node){ if n==nil ||n.prev==nil || n.next==nil || nn==nil ||nn.prev==nil ||nn.next==nil{ return } if n.value>nn.value { prev:=n.prev[level] n.prev[level]=nn nn.next[level]=n nn.prev[level]=prev if prev!=nil{ prev.next[level]=nn } }else{ next:=n.next[level] n.next[level]=nn nn.prev[level]=n nn.next[level]=next if next!=nil{ next.prev[level]=nn } } } func (this *Skiplist) Add(num int) { this.Print() n:=this.SearchPos(num) if n==nil{ this.maxLevel=0 n=NewNode(0,num) this.root=append(this.root,n) return } if n.value==num{ n.count++ return } if this.randomUpgrade(){ this.maxLevel++ nn:=NewNode(this.maxLevel,num) this.root=append(this.root,nn) for i:=1;i<=this.maxLevel-1;i++{ in:=this.root[i] for in!=nil && in.value>num && in.prev!=nil && in.prev[i]!=nil{ in=in.prev[i] } for in!=nil && in.value<num && in.next!=nil && in.next[i]!=nil{ in=in.next[i] } in.InsertLevelNode(i,nn) } n.InsertLevelNode(0,nn) }else{ nn:=NewNode(0,num) n.InsertLevelNode(0,nn) } } func (this*Skiplist)DeleteNode(n*Node,level int){ if n==nil{ return } next:=n.next[level] prev:=n.prev[level] //fmt.Println("next",next,"prev",prev) if prev!=nil{ prev.next[level]=next }else{ this.root[level]=next } if next!=nil{ next.prev[level]=prev } } func (this *Skiplist) Erase(num int) bool { //this.Print() n:=this.SearchPos(num) if n!=nil{ //fmt.Println("erease ",*n) } if n==nil || n.value!=num{ return false } if n.count>1{ n.count-- return true } for i:=0;i<=n.level;i++{ //fmt.Println("-->",i) this.DeleteNode(n,i) } //fmt.Println("-->") //this.Print() //level i 删除了,i+1 肯定删除了,特殊情况,n层都是最高index, count:=0 for level:=this.maxLevel;n.level==this.maxLevel && level>=0 && this.root!=nil && this.root[level]==nil ;level--{ count++ } this.root=this.root[:len(this.root)-count] this.maxLevel-=count n.count-- n.level=-1 n.next=nil n.prev=nil n=nil return true } /** * Your Skiplist object will be instantiated and called as such: * obj := Constructor(); * param_1 := obj.Search(target); * obj.Add(num); * param_3 := obj.Erase(num); */ func main() { obj := Constructor(); obj.Add(0); obj.Add(1); obj.Add(4); obj.Add(1); param_1 := obj.Search(1); fmt.Println(param_1) param_3 := obj.Erase(1); fmt.Println(param_3) }