随机化跳表

跳表

跳表是一种链式数据结构,具有和平衡树相似的插入查找功能,支持 O ( log ⁡ n ) O(\log n) O(logn) 插入,删除,查找,合并
使用的空间是 O ( n ) O(n) O(n)

其形状为每一层有 1 2 \frac 1 2 21 的节点延申到下一层,最顶层除去表头外只有一个节点

一些规定

为了简化代码,此处进行一些简单的定义

type Node struct {//管理数据的节点
	data int //存放的数据
	key  []byte //key值
}
type Element struct {//跳表中的元素
	data    Node 
	hashKey uint64 //进行快速计算的哈希值
	levels  []*Element //跳表某层指向后继的指针
}
type SkipList struct {
	header *Element
}

跳表的查找

从最顶层向下跳跃查找
如果当前层可以找到节点则直接返回,否则考虑到下一层查找
如果当前层并未找到需要的节点,且当前节点和当前节点的后继节点正好包含要查询的 k e y key key,则记录位置,跳转到下一层查找

func (list *SkipList) Find(key []byte) *Node {
	pre := list.header
	hashKey := calcHashKey(key)
	for i := len(list.header.levels) - 1; i >= 0; i-- {
		for nxt := pre.levels[i]; nxt != nil; nxt = pre.levels[i] {
			res := cmp(hashKey, key, nxt)
			if res == 0 {
				return &nxt.data
			} else {
				if res == -1 {
					break
				}
			}
			pre = nxt
		}
	}
	return nil
}

calcHashKey k e y key key 中的前8位拿出来做一个进制哈希,这样可以快速进行大小的比较

func calcHashKey(key []byte) uint64 {
	len := len(key)
	if len > 8 {
		len = 8
	}
	var res uint64
	for i := 0; i < len; i++ {
		res |= uint64(key[i]) << (64 - (i+1)*8)
	}
	return res
}
func (a *Element) calcHashKey() {
	a.hashKey = calcHashKey(a.data.key)
}
func cmp(hashKey uint64, key []byte, b *Element) int {
	if hashKey == b.hashKey {
		return bytes.Compare(key, b.data.key)
	}
	if hashKey < b.hashKey {
		return -1
	} else {
		return 1
	}
}

跳表的插入

找到节点后,记录每层的前驱和后继,插入进去
代码结合后面的随机化跳表给出

随机化跳表

随机化跳表依靠概率保证时间复杂度,普通跳表插入会破坏跳表节点逐层递减的性质
我们让每个节点在每层有 1 2 \frac 1 2 21 的概率向上生长

时间复杂度证明

p p p 表示节点向上生长一层的概率, p = 1 2 p = \frac 1 2 p=21 p = 0.25 p = 0.25 p=0.25 ,都可以
我们设 f ( x ) f(x) f(x) 表示跳表高度至少为 x x x 的概率 (高度从 0 0 0 开始), f ( x ) = p x f(x) = p^x f(x)=px
其期望生长高度为 E ( x ) = ∑ f ( x ) = ∑ x = 0 m a x H e i g h t p x E(x) = \sum f(x) = \sum_{x = 0}^{maxHeight} p^x E(x)=f(x)=x=0maxHeightpx
我们知道这个数列对于 p < 1 p < 1 p<1 是收敛的,则其每层节点呈指数级递减,由于生长是随机的,则其分布也是均匀的

随机化跳表插入

只需要在插入的时候让其随机生长一个高度即可

func (list *SkipList) Insert(p Node) *Node {
	pre := list.header
	hashKey := calcHashKey(p.key)
	var preElemHeaders [MaxLevel]*Element //注意上界,可能需要调整或取min
	for i := len(list.header.levels) - 1; i >= 0; i-- {
		preElemHeaders[i] = pre
		for nxt := pre.levels[i]; nxt != nil; nxt = pre.levels[i] {
			res := cmp(hashKey, p.key, nxt)
			if res == -1 {
				preElemHeaders[i] = pre
				break
			}
			pre = nxt
		}
	}
	len := 0
	for checkUp() {
		len++
	}
	var element *Element = new(Element)
	element.data = p
	element.hashKey = hashKey
	element.levels = make([]*Element, len)
	for i := 0; i < len; i++ {
		element.levels[i] = preElemHeaders[i].levels[i]
		preElemHeaders[i].levels[i] = element
	}
	return nil
}

随机化跳表的合并与删除

合并与删除仅仅需要在上述代码中按层进行普通链表的合并与删除,这里不再放出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值