跳表skip list 代码实现与应用场景

跳表是一种用来增加元素查找速度的数据结构。他是由一条有序的单链表演变而成。

他把单链表由一层变为了多层,越往上,元素数量越少。每个在单链表上的元素,都有一个level,level等于几,就从下往上占几层。这个level是有一个算法随机生成的。(实际每个元素都只有一个,占几层是通过切片指针来实现的)

当进行查找的时候,从头节点开始,往右往下查找元素(如图查找117 118 (图是盗的))。

clip_image007

首先是跳表的架构

//跳表的结构

//节点的value
type SkipValue struct {
	Score int64
	Value interface{}
}


//节点
type skipListNode struct {
	next  []*skipListNode   //指向下一个节点的指针切片  下标对应的是相应的level
	prev  *skipListNode
	Value *SkipValue
}


//跳表的入口
type SkipList struct {
	header, tail *skipListNode  //头 尾
	findcount    int
	count        int
	level        int   //层数
}



type SkipListIterator struct {
	list *SkipList
	node *skipListNode
}

 下图头节点是0  头节点里面有一个next的指针切片,next[0]指向value为1的节点(即指向第0层的下一个元素1),next[1]指向value为2的节点(指向第1层的下一个元素1),next[2]指向value为12的节点,next[3]指向value为12的节点,next[4]指向value为12的节点,next[5]指向nil

该跳表的实现与别的略有不同,这边value最大的尾节点是29,没有占了所有高度

 

单看前几层  结构图如下

 

当需要插入一个元素的时候,首先需要查找到他每一层的前驱节点

代码如下

var update [maxLevel]*skipListNode
x := sl.header   //头节点地址

for i := sl.level - 1; i >= 0; i-- {
	for x.next[i] != nil && x.next[i].Value.Compare(value) < 0 {
		x = x.next[i]
	}
	update[i] = x	
}

 

参照图片,假如需要插入元素15的时候,同时假设他的level为2

先是i=3-1=2,x.next[2]是头节点的指针切片的第3个值,即指向21的指针,x.next[i].Value 的值为21,比15大,不进入下一个for。然后把头节点地址复制给update[i](这边会把每一层的前驱节点都复制到update里,但是后面会根据具体的生成的level来进行赋值)。

继续 i = 1,x.next[1]指向7,x.next[i].Value = 7,7比15小,进入for,然后x指向7,7里的next[1]指向21,21比15大,出循环,同时把7的地址存到update[1]里。

继续i=0,此时x是7,x.next[0]指向14,x.next[i].Value = 14,14比15小,进入for,然后x指向14,14里的next[0]指向21,21比15大,出循环,同时把14的地址存到update[0]里。

 

此时update[0]是指向14的节点,update[1]是指向7的节点,update[2]是指向-1的节点,不难看出,如果15的level是3,则update[i]存的都是15节点每一层对应的前一个节点,但是此时level为2,所有我们只需要update[0]和update[1]就够了。此时便有了以下代码

level := randomLevel()//随机生成的level

x = newskipListNode(level, value)//新生成的节点

for i := 0; i < level; i++ {  //level为2  则只是对update[0],update[1]进行了复制
        
        //新生成节点的指针切片对应层数的值 = 为前驱节点的指针切片对应层数的值
        //比如第二层,前驱节点是7,7的next[2]指向21,此时把指向21的指针复制给新的节点,让新的节点指向21    
	x.next[i] = update[i].next[i]  
        
        //update[i]指的是前驱节点地址,将前驱节点的指针切片的第i层变为指向新增的节点
	update[i].next[i] = x
}

完整的新增节点代码如下

func (sl *SkipList) Insert(value *SkipValue) int {
	var update [maxLevel]*skipListNode
	x := sl.header
	for i := sl.level - 1; i >= 0; i-- {
		for x.next[i] != nil && x.next[i].Value.Compare(value) < 0 {
			x = x.next[i]
		}
		update[i] = x
	}
	if x.next[0] != nil && x.next[0].Value.Compare(value) == 0 { //update
		x.next[0].Value = value
		return 0
	}
	level := randomLevel()
	if level > sl.level {
		for i := sl.level; i < level; i++ {
			update[i] = sl.header
		}
		sl.level = level
	}
	x = newskipListNode(level, value)
	for i := 0; i < level; i++ {
		x.next[i] = update[i].next[i]
		update[i].next[i] = x
	}
	//形成一个双向链表
	if update[0] != sl.header {
		x.prev = update[0]
	}
	if x.next[0] != nil {
		x.next[0].prev = x
	} else {
		sl.tail = x
	}
	sl.count++
	return 1
}

 

查询代码如下

func (sl *SkipList) find(value *SkipValue) *skipListNode {
	x := sl.header
	for i := sl.level - 1; i >= 0; i-- {
		for x.next[i] != nil && x.next[i].Value.Compare(value) < 0 {
			sl.findcount++
			x = x.next[i]
		}
	}
	return x
}



func (sl *SkipList) Find(value *SkipValue) *SkipValue {
	x := sl.find(value)
	if x.next[0] != nil && x.next[0].Value.Compare(value) == 0 {
		return x.next[0].Value
	}
	return nil
}

 

应用场景:

存放交易所的卖单和买单

当存放买单时,由于价格需要从高到低取,当取数据时,从跳表的尾部向前取(该跳表是双向链表)

for data := it.Last(); data != nil; data = it.Prev() {
	t := Tick{Price: value.price, Amount: value.amount}
	info.Buy = append(info.Buy, t)
}

当存放卖单时,价格由低到高排列,当取数据时,从头部开始取

for data := it.First(); data != nil; data = it.Next() {
    	t := Tick{Price: value.price, Amount: value.amount}
	info.Sell = append(info.Sell, t)
}

当插入订单时(通过价格来定位)

	//订单未完成
	//把订单加入到orderlist
	item := &SkipValue{Score: order.GetPrice()}
	var value *SkipValue
	if isbuy {
		value = match.buy.Find(item)
	} else {
		value = match.sell.Find(item)
	}
	var orderlist *OrderList
	if value == nil { //new OrderList
		orderlist = NewOrderList(order.GetPrice())
		item.Value = orderlist
		if isbuy {
			match.buy.Insert(item)
		} else {
			match.sell.Insert(item)
		}
	} else {
		orderlist = value.Value.(*OrderList)
	}

	match.add(orderlist, order)

 

 

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值