跳跃表入门

跳跃表这个货色,始终在据说,但从未手动实现过,所以了解的也不是很透彻。最近闲来无事,用golang实现了一个根底版本,加深一下了解。

跳跃表的逻辑构造如下:

这里不解释根底原理了,网上大把的材料,总结几点加深了解:

  • 跳跃表的底层还是链表,而且是有序链表,在结构跳跃表的时候就必须保证数据有序;
  • 跳跃表用的是空间换工夫的思维;
  • 有点相似有序数组的二分查找;
  • 跳表的查问,插入和删除操作的冀望工夫复杂度都为 O(logN);

代码实现

跳跃表的实现并不固定,能够有很多不同的版本,我这里仅实现了一个根底版本,并没有各种优化操作。

根底构造:

// 单个节点type SkipNode struct {    Val   int    Level int    Nexts []*SkipNode}// 跳跃表type SkipList struct {    Head *SkipNode    //Tail     *SkipNode    MaxLevel int}

源码:

package mainimport (    "errors"    "fmt"    "math/rand"    "time")// 单个节点type SkipNode struct {    Val   int    Level int    Nexts []*SkipNode}// 跳跃表type SkipList struct {    Head *SkipNode    //Tail     *SkipNode    MaxLevel int}// 初始化func (obj *SkipList) Init(maxLevel int) {    obj.MaxLevel = maxLevel    head := new(SkipNode)    head.Level = 1    head.Val = -99999    head.Nexts = make([]*SkipNode, 1, maxLevel)    obj.Head = head}// 获取新节点的最大层数func (obj *SkipList) GetNodeLevel() int {    level := 1    for rand.Intn(2) == 1 {        level++        if obj.MaxLevel <= level {            break        }    }    return level}// 插入节点func (obj *SkipList) insert(val int) error {    preNodes := make([]*SkipNode, obj.Head.Level, obj.Head.Level)    currNode := obj.Head    // 查找定位地位    for i := obj.Head.Level - 1; i >= 0; i-- {        for currNode.Nexts[i] != nil && currNode.Nexts[i].Val < val {            currNode = currNode.Nexts[i]        }        if currNode.Nexts[i] != nil && currNode.Nexts[i].Val == val {            return errors.New("same item")        }        preNodes[i] = currNode    }    // 创立新节点    node := new(SkipNode)    node.Val = val    node.Level = obj.GetNodeLevel()    node.Nexts = make([]*SkipNode, node.Level, node.Level)    for i := 0; i < node.Level; i++ {        if i < obj.Head.Level {            node.Nexts[i] = preNodes[i].Nexts[i]            preNodes[i].Nexts[i] = node        } else {            node.Nexts[i] = nil            obj.Head.Nexts = append(obj.Head.Nexts, node)        }    }    obj.Head.Level = len(obj.Head.Nexts)    return nil}// 查找跳表func (obj *SkipList) find(val int) *SkipNode {    currNode := obj.Head    // 查找定位地位    for i := obj.Head.Level - 1; i >= 0; i-- {        for currNode.Nexts[i] != nil && currNode.Nexts[i].Val < val {            currNode = currNode.Nexts[i]        }        if currNode.Nexts[i] != nil && currNode.Nexts[i].Val == val {            return currNode.Nexts[i]        }    }    return nil}// 删除节点func (obj *SkipList) del(val int) {    preNodes := make([]*SkipNode, obj.Head.Level, obj.Head.Level)    currNode := obj.Head    // 查找定位地位    for i := obj.Head.Level - 1; i >= 0; i-- {        for currNode.Nexts[i] != nil && currNode.Nexts[i].Val < val {            currNode = currNode.Nexts[i]        }        if currNode.Nexts[i] != nil && currNode.Nexts[i].Val == val {            preNodes[i] = currNode        }    }    // 删除节点    for i := 0; i < obj.Head.Level; i++ {        if preNodes[i] == nil {            continue        }        preNodes[i].Nexts[i] = preNodes[i].Nexts[i].Nexts[i]    }}// 打印跳表func (obj *SkipList) printf() {    fmt.Println("skip list:")    node := obj.Head    for i := obj.Head.Level - 1; i >= 0; i-- {        fmt.Printf("    skip list level %d: ", i)        tmpNode := node.Nexts[i]        for tmpNode != nil {            fmt.Printf("%d->", tmpNode.Val)            tmpNode = tmpNode.Nexts[i]        }        fmt.Println("\b\b  ")    }}// 结构func (obj *SkipList) build() {    for i := 1; i < 70; i += 2 {        obj.insert(i)    }}func main() {    rand.Seed(time.Now().UnixNano())    skipObj := SkipList{}    skipObj.Init(10)    skipObj.build()    skipObj.printf()    err := skipObj.insert(3)    if err != nil {        fmt.Printf("insert 3 failed, %s\n", err.Error())    }    res := skipObj.find(37)    if res == nil {        fmt.Printf("not found 37\n")    } else {        fmt.Printf("found 37, %+v\n", res)    }    skipObj.del(37)    skipObj.del(33)    skipObj.del(4)    skipObj.printf()    res = skipObj.find(37)    if res == nil {        fmt.Printf("not found 37\n")    } else {        fmt.Printf("found 37, %+v\n", res)    }}

运行后果如下: