背景:
相信大家都知道排行榜,在很多场景里都需要用到排行榜功能,尤其是游戏中!之前在了解排行榜实现机制的时候,在网上看得最多的答复便是使用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)
}
small_lei_it 技术无止境,追求更高。