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 }