SkipListMap

package SortedMap

import (
	"fmt"
	"math/rand"
	"testing"
	"time"
)

/*
跳表  skip List ,可以实现有序表的所有操作
O(logN) 实现机制和二叉树没关系
跳表好实现,思想先进,跳表实现的有序思路,要比平衡二叉树先进

跳表,是用Node串成的, Node 有多条往外指的指针
一开始的第一个节点,是系统最小值,Node上也得放key 需要支持排序
跳表最左侧的节点认为是弃而不用的,以后增加的所有的key 都要比 最左边的头节点key = nil 标记一个最小的key
一开始,节点只有一条往外指的指针,作为最左侧,node的往外指的指针可以扩展



-------------------
|  nil  smallest  |
|                 |---> nil
-------------------
      left






最左侧节点,拥有全局最小值,他的next指针指向nil
假设加入一个数据  3 : "abc"


-------------------
|  key: 3         |
|  val:"abc"      |---> nil
-------------------


生成[key:3, val:"abc"] 记录,每个节点一开始有几层节点呢
最开始有几层的记录,用随机的方式决定  rand.Float64() ->
<= 0.5 认为是0
>  0.5 认为是1

[key:3, val:"abc"] 一开始有一条往外指的指针
在有一条指针的基础上 随机
如果随机的是0  则加一条指针, 再循环,直到遇见1为止

for 随机数 == 0 {
   加一条指针
}

算法上讨论的是N无穷大的情况,所以暂时不限定层数

一旦 确定 随机出多少层,对于自己这个节点的层数,绝对不会再扩展了
扩展的永远的是 全局一开始就有的节点


-------------------
|  key: 3         |
|  val:"abc"      |---> nil
|  next           |---> nil
|                 |---> nil
-------------------

假设某个节点随机出了三层指针


-------------------
|  nil  smallest  |
|                 |---> nil
-------------------
      left
最左侧节点向外指针的层数 一定要保持和 所有节点中 指针数最大的 强同步

-------------------
|  nil  smallest  |
|                 |---> nil
|                 |---> nil
|                 |---> nil
-------------------
      left

然后开始增加节点

从最上层找到  最早的 > 3 的key  或 找到最晚的 <= 3 的key, 没有,则让该层指针,直接指向 3 节点
往下走, 在第二层上遍历 继续找 最晚的 <= 3 的key, 没有,则让该层指针,直接指向 3 节点
往下走, 在第三层上遍历 继续找 最晚的 <= 3 的key, 没有,则让该层指针,直接指向 3 节点











-------------------    -------------------
|  nil  smallest  |    |  key: 3         |
|                 |--->|  val:"abc"      |---> nil
|  next           |--->|  next           |---> nil
|                 |--->|                 |---> nil
-------------------    -------------------
      left                     3


有高层的节点一定有低层,避免出现断层


5节点加入   假设只随机出了两层指针
从头节点最高层指针遍历,  找到<=5 最晚的节点,   此时来到3了, 3 再往下找就没有了
因为5没有3层,所以 3的指针不指向 5
但是在3的内部,跳转到下层指针
高层往右走,右到不能再右,往下跳
连接5节点


-------------------    -------------------        -------------------
|  nil  smallest  |    |  key: 3         |        |  key: 5         |
|                 |--->|  val:"abc"      |-->nil  |  val:"def"      |
|  next           |--->|  next           |------> |  next           |------> nil
|                 |--->|                 |------> |                 |------> nil
-------------------    -------------------        -------------------
      left                     3                          5


假如加入节点2 只 随机出一层节点

从头节点遍历

-------------------                               -------------------        -------------------
|  nil  smallest  |                               |  key: 3         |        |  key: 5         |
|                 |------------------------------>|  val:"abc"      |-->nil  |  val:"def"      |
|  next           |------------------------------>|  next           |------> |  next           |------> nil
|                 |---    ---------------    ---->|                 |------> |                 |------> nil
-------------------  |    |             |    |    -------------------        -------------------
      left           |    |  key:2      |    |              3                          5
                     |    |  val:"hnn"  |    |
                     |    |  next       |    |
                     ---->|             |----
                          ---------------

查找的顺序和 插入的一样



和最高层节点保持一致,随机出更高的层数,左节点的指针层数就扩充
-----
|nil|                                                                 -----
| 5 |---------------------------------------------------------------->| 7 |----> nil
|   |                                   -----                         |   |
| 4 |---------------------------------->| 4 |------------------------>|   |----> nil
|   |               -----               |   |     -----               |   |
| 3 |-------------->| 2 |-------------->|   |---->| 5 |-------------->|   |----> nil
|   |               |   |     -----     |   |     |   |     -----     |   |
| 2 |-------------->|   |---->| 3 |---->|   |---->|   |---->| 6 |---->|   |----> nil
|   |     -----     |   |     |   |     |   |     |   |     |   |     |   |
| 1 |---->| 1 |---->|   |---->|   |---->|   |---->|   |---->|   |---->|   |----> nil
-----     -----     -----     -----     -----     -----     -----     -----
  左        1         2         3         4         5         6         7

查找6  如何操作?

由高到低,遍历左节点的指针数组,看能否沿着最高层指针向右移动,不能,因为最高层指针指向节点的值为7,7 > 6
来到左节点下一层指针,即从左节点5层节点位置往下跳,来到第4层,第4层指针指向的节点的值为4, 4 < 6 所以能往右跳,来到值为4节点
值为4的节点,指针数组当前层不能往右跳   因为指向值为7的节点, 7 > 6
4节点从当前层往下跳来到第3层  第三层指针指向 节点5   5 < 6
5最高层,不能向右移动,
5降层,来到第2层指针  指向 6  找到了,返回


只要是找,永远从最高层开始

最底层一定有所有数据

假设加N记录,
第1层有N个记录
第2层的期望是N/2
第3层的期望是N/4
第4层的期望是N/8

由高层到低层建立了索引关系
从高层往底层找,越过了很多东西
0------->0
         |
         0--------->0


右 -> 下  等同于在树上走出了一条路径
高层节点越过了一个,底层节点就越过了一大堆
为什么最终能收敛到O(logN),因为和数据状况无关
思想先进:有点像快排,虽然随机出来的一个数,状况不确定,虽然长期期望难求,但是求完很低




*/

type KEY interface {
	CompareTo(v KEY) int
}

type VALUE interface{}

// 跳表的节点定义
type SkipListNode struct {
	key       KEY             // 可比较
	value     VALUE           // 伴随数据
	nextNodes []*SkipListNode // 扩充往外指的指针
}

func NewSkipListNode(key KEY, value VALUE) *SkipListNode {
	return &SkipListNode{
		key:       key,
		value:     value,
		nextNodes: make([]*SkipListNode, 0),
	}
}

// 遍历的时候,如果是往右遍历到的nil(next == nil), 遍历结束
// 头(nil), 头节点的nil,认为最小
// node  -> 头,node(nil, "")  node.isKeyLess(!nil)  true
// node里面的key是否比otherKey小,true,不是false
func (sk *SkipListNode) isKeyLess(otherKey KEY) bool {
	//  otherKey == nil -> false    // sk.key == nil 认为 otherKey  比  sk.key要小
	return otherKey != nil && (sk.key == nil || sk.key.CompareTo(otherKey) < 0)
}

func (sk *SkipListNode) isKeyEqual(otherKey KEY) bool {
	return sk.key == nil && otherKey == nil ||
		sk.key != nil && otherKey != nil && sk.key.CompareTo(otherKey) == 0
}

type SkipListMap struct {
	PROBABILITY float64       //可能性
	head        *SkipListNode //头
	size        int           //上边有多少不同的key
	maxLevel    int           //最大高度
}

func NewSkipListMap() *SkipListMap {
	head := NewSkipListNode(nil, nil)
	head.nextNodes = make([]*SkipListNode, 1, 1) // 对于全局最左节点,让他拥有一层节点,规定最底层的节点叫0层节点
	return &SkipListMap{
		PROBABILITY: 0.5, //可能性: < 0.5 继续做,>= 0.5 停
		head:        head,
		size:        0,
		maxLevel:    0,
	}
}

// 垂直跳跃
// 从最高层开始,一路找下去
// 最终,找到第0层小于key的最右节点
func (skp *SkipListMap) mostRightLessNodeInTree(key KEY) *SkipListNode {
	if key == nil {
		return nil
	}
	cur := skp.head                                  // 最左侧节点一定拥有最大的层数
	for level := skp.maxLevel; level >= 0; level-- { //从最高层开始,往下层跳,cur  level  -> level-1,  每次下降一层,因为不知道降下去那层还能不能再往右了
		cur = skp.mostRightLessNodeInLevel(key, cur, level) // 非递归
	}
	return cur
}

// 水平跳跃
// 在level层里,如何往右移动,同一层
// 现在来到的节点是cur,来到了cur的level层,在level层上,找到小于key最后一个节点并返回
func (skp *SkipListMap) mostRightLessNodeInLevel(key KEY, cur *SkipListNode, level int) *SkipListNode {
	next := cur.nextNodes[level]
	for next != nil && next.isKeyLess(key) { // 人为控制 isKeyLess() 不传入nil
		cur = next
		next = cur.nextNodes[level]
	}
	return cur
}

func (skp *SkipListMap) containsKey(key KEY) bool {
	if key == nil {
		return false
	}
	less := skp.mostRightLessNodeInTree(key)
	next := less.nextNodes[0]
	return next != nil && next.isKeyEqual(key)
}

// 新增或改value
func (skp *SkipListMap) put(key KEY, value VALUE) {
	if key == nil {
		return
	}

	// 0层上,最右一个,小于 key的 Node 他的下一个 不等于key 就大于 key
	less := skp.mostRightLessNodeInTree(key) // 在树中找到小于key的最右节点  O(logN)
	find := less.nextNodes[0]
	if find != nil && find.isKeyEqual(key) { // 改Value的操作
		find.value = value
	} else { // find == nil   8   7   9      // 插入的操作
		skp.size++

		newNodeLevel := 0
		rand.Seed(time.Now().UnixNano())
		for rand.Float64() < skp.PROBABILITY { // 随机层数
			newNodeLevel++
		}

		// newNodeLevel
		for newNodeLevel > skp.maxLevel { // 如果新随机出的层数 大于 最左节点的最大层数, 填充最左层节点
			skp.head.nextNodes = append(skp.head.nextNodes, (*SkipListNode)(nil))
			skp.maxLevel++
		}

		newNode := NewSkipListNode(key, value)
		newNode.nextNodes = make([]*SkipListNode, newNodeLevel+1, newNodeLevel+1) // 原本有一条指针,再加上 随机出来的Level,就是newNode的指针条数

		pre := skp.head
		for level := skp.maxLevel; level >= 0; level-- { // 在树上运动
			// 在level 层中,找到最右的 小于 key 的节点
			pre = skp.mostRightLessNodeInLevel(key, pre, level)
			if level <= newNodeLevel { // 新节点有这一层   就插在 pre 和 next 之间
				newNode.nextNodes[level] = pre.nextNodes[level]
				pre.nextNodes[level] = newNode
			}
		} //目的是 让底层的节点通过高层的索引尽快的走到合适的位置
	}
}

func (skp *SkipListMap) get(key KEY) VALUE {
	if key == nil {
		return nil
	}
	less := skp.mostRightLessNodeInTree(key)
	next := less.nextNodes[0] // 最底层节点
	if next != nil && next.isKeyEqual(key) {
		return next.value
	}
	return nil
}

func (skp *SkipListMap) remove(key KEY) {
	if !skp.containsKey(key) {
		return
	}

	skp.size--
	level := skp.maxLevel
	pre := skp.head
	for level >= 0 {
		pre = skp.mostRightLessNodeInLevel(key, pre, level)
		next := pre.nextNodes[level]
		// 1)在这一层中,pre下一个就是key
		// 2)在这一层中,pre的下一个key是>要删除key
		if next != nil && next.isKeyEqual(key) {
			// free delete node memory -> C++
			// level : pre -> next(key) -> ...
			pre.nextNodes[level] = next.nextNodes[level]  // 我把我的next指向下一层的下一层
		}
		// 在level层只有一个节点了,就是默认节点head
		if level != 0 && pre == skp.head && pre.nextNodes[level] == nil {
			//skp.head.nextNodes.remove(level)
			skp.head.nextNodes = skp.head.nextNodes[:0]
			skp.head.nextNodes = append(skp.head.nextNodes[:level], skp.head.nextNodes[level+1:]...)
			skp.maxLevel--
		}
		level--
	}
}

func (skp *SkipListMap) firstKey() KEY {
	if skp.head.nextNodes[0] != nil {
		return skp.head.nextNodes[0].key
	}
	return nil
}

func (skp *SkipListMap) lastKey() KEY {
	level := skp.maxLevel
	cur := skp.head
	for level >= 0 {
		next := cur.nextNodes[level]
		for next != nil {
			cur = next
			next = cur.nextNodes[level]
		}
		level--
	}
	return cur.key
}

func (skp *SkipListMap) ceilingKey(key KEY) KEY {
	if key == nil {
		return nil
	}
	less := skp.mostRightLessNodeInTree(key)
	next := less.nextNodes[0]
	if next != nil {
		return next.key
	}
	return nil
}

func (skp *SkipListMap) floorKey(key KEY) KEY {
	if key == nil {
		return nil
	}
	less := skp.mostRightLessNodeInTree(key)
	next := less.nextNodes[0]
	if next != nil && next.isKeyEqual(key) {
		return next.key
	}
	return less.key
}

func (skp *SkipListMap) Size() int {
	return skp.size
}

func (skp *SkipListMap) printAll() {
	for i := skp.maxLevel; i >= 0; i-- {
		fmt.Print("Level ", i, " : ")
		cur := skp.head
		for cur.nextNodes[i] != nil {
			next := cur.nextNodes[i]
			fmt.Print("(", next.key, " , ", next.value, ") ")
			cur = next
		}
		fmt.Println()
	}
}

func TestNode(t *testing.T) {
	n := NewSkipListNode(nil, nil)
	t.Log(len(n.nextNodes))
}

type student struct {
	Name string
	Age  int
}

func (s student)CompareTo(val KEY) int {
	if s.Name > val.(student).Name {
		return 1
	}else if s.Name < val.(student).Name {
		return -1
	}
	return 0
}

func TestSkipListMap(t *testing.T) {
	s1 := student{"张1", 0}
	s2 := student{"张2", 1}
	s3 := student{"张3", 2}
	s4 := student{"张4", 3}
	s5 := student{"张5", 4}
	s6 := student{"张6", 5}
	skp := NewSkipListMap()
	skp.put(s1, "1")
	skp.put(s2, "2")
	skp.put(s3, "3")
	skp.put(s4, "4")
	skp.put(s5, "5")
	skp.put(s6, "6")
	skp.printAll()
	skp.remove(s2)
	skp.printAll()
}

注:本人不是算法的原作者,我只是用Go重写了Java的代码,参照该地址,版权归原作者所有!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

metabit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值