github.com/benz9527/toy-box/algo@v0.0.0-20240221120937-66c0c6bd5abd/list/classic_skip_list.go (about)

     1  package list
     2  
     3  import (
     4  	"math/rand"
     5  	"sync/atomic"
     6  	"time"
     7  )
     8  
     9  var (
    10  	_ SkipListNodeElement[struct{}] = (*classicSkipListNodeElement[struct{}])(nil) // Type check assertion
    11  	_ SkipListLevel[struct{}]       = (*classicSkipListLevel[struct{}])(nil)       // Type check assertion
    12  	_ SkipList[struct{}]            = (*classicSkipList[struct{}])(nil)            // Type check assertion
    13  )
    14  
    15  type classicSkipListNodeElement[E comparable] struct {
    16  	object E
    17  	// 指向垂直方向上的下一个结点,通常 level 为 0 的结点,没有 vBackward;但是作为 levels 的node,一定有 vBackward
    18  	// 但是也只有最底层的才需要设置 vBackward,因为 levels 是和 vBackward 一起的
    19  	vBackward SkipListNodeElement[E]
    20  	// 当前结点作为非索引部分时(单纯存放数据),levels 为空
    21  	// 当前结点包含索引数据时,levels[0] 就已经是索引了,和哨兵的 levels[0] 是不一样的
    22  	levels []SkipListLevel[E]
    23  }
    24  
    25  func newClassicSkipListNodeElement[E comparable](level int, obj E) SkipListNodeElement[E] {
    26  	e := &classicSkipListNodeElement[E]{
    27  		object: obj,
    28  		levels: make([]SkipListLevel[E], level), // 一开始就把每一层的间距都设置为 0,也就是一开始就做好了数据分配
    29  	}
    30  	for i := 0; i < level; i++ {
    31  		e.levels[i] = newClassicSkipListLevel[E](0, nil)
    32  	}
    33  	return e
    34  }
    35  
    36  func (e *classicSkipListNodeElement[E]) GetObject() E {
    37  	return e.object
    38  }
    39  
    40  func (e *classicSkipListNodeElement[E]) GetVerticalBackward() SkipListNodeElement[E] {
    41  	return e.vBackward
    42  }
    43  
    44  func (e *classicSkipListNodeElement[E]) SetVerticalBackward(backward SkipListNodeElement[E]) {
    45  	e.vBackward = backward
    46  }
    47  
    48  func (e *classicSkipListNodeElement[E]) GetLevels() []SkipListLevel[E] {
    49  	return e.levels
    50  }
    51  
    52  func (e *classicSkipListNodeElement[E]) Free() {
    53  	e.object = *new(E)
    54  	e.vBackward = nil
    55  	e.levels = nil
    56  }
    57  
    58  type classicSkipListLevel[E comparable] struct {
    59  	span     int64                  // 间距数量
    60  	hForward SkipListNodeElement[E] // 指向水平方向的下一个结点
    61  }
    62  
    63  func newClassicSkipListLevel[E comparable](span int64, forward SkipListNodeElement[E]) SkipListLevel[E] {
    64  	return &classicSkipListLevel[E]{
    65  		span:     span,
    66  		hForward: forward,
    67  	}
    68  }
    69  
    70  func (lvl *classicSkipListLevel[E]) GetSpan() int64 {
    71  	return atomic.LoadInt64(&lvl.span)
    72  }
    73  
    74  func (lvl *classicSkipListLevel[E]) SetSpan(span int64) {
    75  	atomic.StoreInt64(&lvl.span, span)
    76  }
    77  
    78  func (lvl *classicSkipListLevel[E]) GetHorizontalForward() SkipListNodeElement[E] {
    79  	return lvl.hForward
    80  }
    81  
    82  func (lvl *classicSkipListLevel[E]) SetHorizontalForward(forward SkipListNodeElement[E]) {
    83  	lvl.hForward = forward
    84  }
    85  
    86  type classicSkipList[E comparable] struct {
    87  	// 当前 skip list 实际使用了的最大层数,最大不会超过 ClassicSkipListMaxLevel
    88  	// 这个类本身是不包含节点的,这就是个哨兵,用来指向分散在堆中的结点
    89  	// 所以 head.levels[0].hForward 指向的是第一个结点,而这一层级的所有结点
    90  	// 就是完整的单向链表,从 1 开始的层级才是索引
    91  	level           int
    92  	len             int64
    93  	head            SkipListNodeElement[E] // 哨兵结点
    94  	tail            SkipListNodeElement[E] // 哨兵结点
    95  	localCompareTo  compareTo[E]
    96  	randomGenerator *rand.Rand
    97  }
    98  
    99  func NewClassicSkipList[E comparable](compareTo compareTo[E]) SkipList[E] {
   100  	sl := &classicSkipList[E]{
   101  		level:           1,
   102  		len:             0,
   103  		localCompareTo:  compareTo,
   104  		randomGenerator: rand.New(rand.NewSource(time.Now().Unix())),
   105  	}
   106  	sl.head = newClassicSkipListNodeElement[E](ClassicSkipListMaxLevel, *new(E))
   107  	// 哨兵头结点的层数一定是 ClassicSkipListMaxLevel
   108  	// 并且做好了初始化,每一层的间距都是 0
   109  	// 防止后续插入的时候,出现空指针异常
   110  	for i := 0; i < ClassicSkipListMaxLevel; i++ {
   111  		sl.head.GetLevels()[i].SetSpan(0)
   112  		sl.head.GetLevels()[i].SetHorizontalForward(nil)
   113  	}
   114  	sl.head.SetVerticalBackward(nil)
   115  	sl.tail = nil
   116  	return sl
   117  }
   118  
   119  func (sl *classicSkipList[E]) randomLevel() int {
   120  	level := 1
   121  	for float64(sl.randomGenerator.Int63()&0xFFFF) < ClassicSkipListProbability*0xFFFF {
   122  		level += 1
   123  	}
   124  	if level < ClassicSkipListMaxLevel {
   125  		return level
   126  	}
   127  	return ClassicSkipListMaxLevel
   128  }
   129  
   130  func (sl *classicSkipList[E]) GetLevel() int {
   131  	return sl.level
   132  }
   133  
   134  func (sl *classicSkipList[E]) Len() int64 {
   135  	return atomic.LoadInt64(&sl.len)
   136  }
   137  
   138  func (sl *classicSkipList[E]) Insert(obj E) SkipListNodeElement[E] {
   139  	var (
   140  		update     [ClassicSkipListMaxLevel]SkipListNodeElement[E]
   141  		x          SkipListNodeElement[E]
   142  		levelSpans [ClassicSkipListMaxLevel]int64 // levelSpans[i] 表示哨兵 levels 第 i 层的间距,第 0 层是数据层
   143  		levelIdx   int
   144  		level      int
   145  	)
   146  	// 临时对象,获取哨兵结点,从头开始遍历
   147  	x = sl.head
   148  	// 从最高层开始查找当前新元素的插入位置
   149  	for levelIdx = sl.level - 1; levelIdx >= 0; levelIdx-- {
   150  		if levelIdx == sl.level-1 {
   151  			// 第一次遍历,必然是会进入到这里,当前最高层的间距为 0
   152  			// 因为这里不计算第 0 层的间距,需要高度退一层
   153  			levelSpans[levelIdx] = 0
   154  		} else {
   155  			// 从第二次遍历开始,当前层的间距为上一层索引结点的间距
   156  			levelSpans[levelIdx] = levelSpans[levelIdx+1]
   157  		}
   158  
   159  		// 1. 第一次遍历且跳表元素为空,如果当前索引没有下一个索引结点,排名(间距)不变
   160  		// 2. 第N次遍历,判断当前索引节点是否有下一个索引结点,如果有,则比较当前新元素和下一个索引节点的指向元素大小
   161  		//    如果当前新元素大于下一个索引节点的指向元素,则当前新元素的排名为当前索引节点的排名
   162  		//    所谓排名,就是当前索引节点到下一个索引节点的距离
   163  		for x.GetLevels()[levelIdx].GetHorizontalForward() != nil &&
   164  			sl.localCompareTo(x.GetLevels()[levelIdx].GetHorizontalForward().GetObject(), obj) < 0 {
   165  			// 更新当前索引节点到下一个索引节点的距离,即排名
   166  			levelSpans[levelIdx] += x.GetLevels()[levelIdx].GetSpan()
   167  			// 当前新元素的值比较大,更新临时结点为当前比较的结点,继续向后遍历,直到找到下一个比
   168  			// 当前新元素大的索引节点,作为右边界
   169  			x = x.GetLevels()[levelIdx].GetHorizontalForward()
   170  		}
   171  		// 找到了右边界,暂存右边界
   172  		// 没找到右边界,也需要暂存当前临时结点,因为当前临时结点是当前新元素的左边界
   173  		// 继续往下一层遍历
   174  		update[levelIdx] = x // 这里就有可能让 update[0] 指向哨兵头结点,因为第一次跳表为空或者是没有下一个元素(末尾了)
   175  	}
   176  	// update 的最后一层必然是数据部分,而不是索引部分
   177  
   178  	// 到这里就相当于找到了当前新元素的左边界和右边界
   179  	// 如果当前新元素的值和右边界的值相等,则不需要插入
   180  	if x.GetLevels()[0].GetHorizontalForward() != nil &&
   181  		sl.localCompareTo(x.GetLevels()[0].GetHorizontalForward().GetObject(), obj) == 0 {
   182  		return nil
   183  	}
   184  
   185  	// 元素不存在,需要插入
   186  	// 要生成随机层数
   187  	level = sl.randomLevel()
   188  	if level > sl.level {
   189  		// 如果随机层数大于当前 skip list 的最大层数,则需要更新当前 skip list 的最大层数
   190  		for lvl := sl.level; lvl < level; lvl++ {
   191  			// 从当前 skip list 的最大层数开始,更新每一层的间距
   192  			levelSpans[lvl] = 0   // 当前层的间距,相当于之后的运算中不需要减去这一层的数量
   193  			update[lvl] = sl.head // 新增的层,指向头哨兵结点
   194  			// 只有哨兵头结点的层数是 ClassicSkipListMaxLevel,才能进行遍历
   195  			update[lvl].GetLevels()[lvl].SetSpan(sl.len)
   196  		}
   197  		// 更新当前 skip list 实际使用了的最大层数
   198  		sl.level = level
   199  	}
   200  
   201  	// 生成新的结点,准备插入。一个需要插入的元素,一定是会从最底层开始
   202  	x = newClassicSkipListNodeElement[E](level, obj)
   203  	// 从最低层开始,更新每一层的间距
   204  	for levelIdx = 0; levelIdx < level; levelIdx++ {
   205  		// 当前结点的当前层指向
   206  		// update 保留了之前每一层遍历右边界结果,相当于是路径记录
   207  		// 因为新增了索引,需要调整索引之间的指向,类似于双向链表的指针调整
   208  		// 直接是指针复制,需要调整指针指向
   209  		// 如果是第一次插入且跳表为空,下一个指向肯定是 nil
   210  		x.GetLevels()[levelIdx].SetHorizontalForward(update[levelIdx].GetLevels()[levelIdx].GetHorizontalForward())
   211  		update[levelIdx].GetLevels()[levelIdx].SetHorizontalForward(x)
   212  
   213  		// 插入新的索引之后,需要重新调整各个层的间距数量(旧索引1 ----> 新索引1 ----> 旧索引2)
   214  		// levelSpans[0] 必须大于 levelSpans[levelIdx],才能算出差值
   215  		x.GetLevels()[levelIdx].SetSpan(update[levelIdx].GetLevels()[levelIdx].GetSpan() - (levelSpans[0] - levelSpans[levelIdx]))
   216  		update[levelIdx].GetLevels()[levelIdx].SetSpan(levelSpans[0] - levelSpans[levelIdx] + 1)
   217  	}
   218  
   219  	// 逐层+1更新更高级的索引层的间距,因为 update 在这就是一个路径,而且路径会指向不同的索引和不同的层
   220  	// 前面更新了是底部的索引层的信息,如果当前插入的结点的层数小于经过路径的层数
   221  	// 就会存在没有被更新到的索引层
   222  	for levelIdx = level; levelIdx < sl.level; levelIdx++ {
   223  		update[levelIdx].GetLevels()[levelIdx].SetSpan(update[levelIdx].GetLevels()[levelIdx].GetSpan() + 1)
   224  	}
   225  
   226  	if update[0] == sl.head {
   227  		// 如果最底层的索引结点指向了头结点
   228  		// 1. 空跳表
   229  		// 2. 最后一个元素
   230  		x.SetVerticalBackward(nil) // 当前结点的下一个结点设置为空
   231  	} else {
   232  		x.SetVerticalBackward(update[0]) // 设置为下一个元素
   233  	}
   234  
   235  	if x.GetLevels()[0].GetHorizontalForward() != nil {
   236  		// 通常来说,插入了新元素,而且有索引,一定是走这里
   237  		// 因为前面是指针的复制,原本 vBackward 的指向还是旧索引1
   238  		// 只有最底层才需要调整 vBackward,因为 levels 是和 vBackward 一起的
   239  		x.GetLevels()[0].GetHorizontalForward().SetVerticalBackward(x)
   240  	} else {
   241  		// 如果是最后一个元素,需要更改哨兵尾结点的指向
   242  		sl.tail = x
   243  	}
   244  	sl.len++
   245  	return x
   246  }
   247  
   248  func (sl *classicSkipList[E]) Remove(obj E) SkipListNodeElement[E] {
   249  	var (
   250  		update [ClassicSkipListMaxLevel]SkipListNodeElement[E]
   251  		x      SkipListNodeElement[E]
   252  		idx    int
   253  	)
   254  	// 临时对象,获取哨兵结点,从头开始遍历
   255  	x = sl.head
   256  	for idx = sl.level - 1; idx >= 0; idx-- {
   257  		// 从最高层开始查找当前待删除元素的位置
   258  		for x.GetLevels()[idx].GetHorizontalForward() != nil &&
   259  			sl.localCompareTo(x.GetLevels()[idx].GetHorizontalForward().GetObject(), obj) < 0 {
   260  			// 如果当前索引节点的下一个索引节点的值小于待删除元素的值
   261  			// 则当前索引节点的下一个索引节点就是待删除元素的右边界
   262  			x = x.GetLevels()[idx].GetHorizontalForward()
   263  		}
   264  		// 不管找没找到右边界,都需要记录当前索引节点
   265  		update[idx] = x
   266  		// 转入下一次的遍历
   267  	}
   268  
   269  	// 到这里就相当于找到了当前待删除元素的左边界和右边界
   270  	x = x.GetLevels()[0].GetHorizontalForward()
   271  	if x != nil && sl.localCompareTo(x.GetObject(), obj) == 0 {
   272  		// 找到了待删除元素
   273  		sl.deleteNode(x, update)
   274  		return x
   275  	}
   276  	// 没有找到待删除元素
   277  	return nil
   278  }
   279  
   280  func (sl *classicSkipList[E]) deleteNode(x SkipListNodeElement[E], update [32]SkipListNodeElement[E]) {
   281  	var idx int
   282  	// 从底层开始,逐级向上调整索引指向和间距
   283  	for idx = 0; idx < sl.level; idx++ {
   284  		if update[idx].GetLevels()[idx].GetHorizontalForward() == x {
   285  			// 如果当前索引节点的下一个索引节点就是待删除元素
   286  			// 调整当前索引节点的下一个索引节点为待删除元素的下一个索引节点
   287  			// 调整当前索引节点的间距为待删除元素的间距
   288  			update[idx].GetLevels()[idx].SetSpan(update[idx].GetLevels()[idx].GetSpan() + x.GetLevels()[idx].GetSpan() - 1)
   289  			update[idx].GetLevels()[idx].SetHorizontalForward(x.GetLevels()[idx].GetHorizontalForward())
   290  		} else {
   291  			// 如果当前索引节点的下一个索引节点不是待删除元素
   292  			// 调整当前索引节点的间距为待删除元素的间距
   293  			update[idx].GetLevels()[idx].SetSpan(update[idx].GetLevels()[idx].GetSpan() - 1)
   294  		}
   295  	}
   296  	if x.GetLevels()[0].GetHorizontalForward() != nil {
   297  		// 如果待删除元素的下一个索引节点不为空,也就是不是最后一个元素
   298  		// 调整待删除元素的下一个索引节点的 vBackward 为待删除元素的 vBackward
   299  		x.GetLevels()[0].GetHorizontalForward().SetVerticalBackward(x.GetVerticalBackward())
   300  	} else {
   301  		// 如果待删除元素的下一个索引节点为空,也就是是最后一个元素
   302  		// 调整哨兵尾结点的指向为待删除元素的 vBackward
   303  		sl.tail = x.GetVerticalBackward()
   304  	}
   305  	for sl.level > 1 && sl.head.GetLevels()[sl.level-1].GetHorizontalForward() == nil {
   306  		sl.level--
   307  	}
   308  	sl.len--
   309  }
   310  
   311  func (sl *classicSkipList[E]) Find(obj E) SkipListNodeElement[E] {
   312  	var (
   313  		x   SkipListNodeElement[E]
   314  		idx int
   315  	)
   316  	// 临时对象,获取哨兵结点,从头开始遍历
   317  	x = sl.head
   318  	// 从最高层开始查找当前匹配的元素的位置
   319  	for idx = sl.level - 1; idx >= 0; idx-- {
   320  		for x.GetLevels()[idx].GetHorizontalForward() != nil &&
   321  			sl.localCompareTo(x.GetLevels()[idx].GetHorizontalForward().GetObject(), obj) < 0 {
   322  			// 还是和插入一样,找到右边界
   323  			x = x.GetLevels()[idx].GetHorizontalForward()
   324  		}
   325  		// 转入下一次的遍历
   326  	}
   327  	// 到这里就相当于找到了当前待删除元素的左边界和右边界
   328  	x = x.GetLevels()[0].GetHorizontalForward()
   329  	// 如果找到了右边界,且右边界的值和待查找元素的值相等,则找到了
   330  	if x != nil && sl.localCompareTo(x.GetObject(), obj) == 0 {
   331  		return x
   332  	}
   333  	// 没有找到
   334  	return nil
   335  }
   336  
   337  func (sl *classicSkipList[E]) PopHead() (obj E) {
   338  	x := sl.head
   339  	x = x.GetLevels()[0].GetHorizontalForward()
   340  	if x == nil {
   341  		return obj
   342  	}
   343  	obj = x.GetObject()
   344  	sl.Remove(obj)
   345  	return
   346  }
   347  
   348  func (sl *classicSkipList[E]) PopTail() (obj E) {
   349  	x := sl.tail
   350  	if x == nil {
   351  		return *new(E)
   352  	}
   353  	obj = x.GetObject()
   354  	sl.Remove(obj)
   355  	return
   356  }
   357  
   358  func (sl *classicSkipList[E]) Free() {
   359  	var (
   360  		x, next SkipListNodeElement[E]
   361  		idx     int
   362  	)
   363  	x = sl.head.GetLevels()[0].GetHorizontalForward()
   364  	for x != nil {
   365  		next = x.GetLevels()[0].GetHorizontalForward()
   366  		x.Free()
   367  		x = nil
   368  		x = next
   369  	}
   370  	for idx = 0; idx < ClassicSkipListMaxLevel; idx++ {
   371  		sl.head.GetLevels()[idx].SetHorizontalForward(nil)
   372  		sl.head.GetLevels()[idx].SetSpan(0)
   373  	}
   374  	sl.tail = nil
   375  	sl.level = 0
   376  	sl.len = 0
   377  }
   378  
   379  func (sl *classicSkipList[E]) ForEach(fn func(idx int64, v E)) {
   380  	var (
   381  		x   SkipListNodeElement[E]
   382  		idx int64
   383  	)
   384  	x = sl.head.GetLevels()[0].GetHorizontalForward()
   385  	for x != nil {
   386  		next := x.GetLevels()[0].GetHorizontalForward()
   387  		fn(idx, x.GetObject())
   388  		idx++
   389  		x = next
   390  	}
   391  }