[H数据布局] lc1206. 计划跳表(模仿+数据布局+跳表实现+良好博文)

[复制链接]
发表于 2025-10-26 00:27:05 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
1. 标题泉源

链接:1206. 计划跳表

干系博文:


  • 良好博主:张铁蕾 — Redis内部数据布局详解(6)——skiplist
  • 良好题解:C++ 手写跳表最简朴的实现方式,思绪详解附具体代码解释【1206. 计划跳表】
题单:


  • 待增补
2. 标题剖析

本题是一个非常良好的数据布局,跳表。
其在 Redis 和 levelDB 中都有用到。其计划头脑和实用场景见:良好博主:张铁蕾 — Redis内部数据布局详解(6)——skiplist 博文即可。
这里我仅做 golang 版本的代码实现,算是一个算法模板。



  • 时间复杂度:                                   O                         (                         log                         ⁡                         n                         )                              O(\log n)                  O(logn)
  • 空间复杂度:                                   O                         (                         n                         )                              O(n)                  O(n)

  1. const LEVEL = 8         // 经验值,本题跳表层级 8 层,Redis 32 层。0层为最下层包含所有元素。7层为最上层元素最少
  2. type Node struct {      // 跳表节点结构体
  3.     val int      // 节点值
  4.     ns []*Node   // 节点在每一层所指向的下一个 next 节点列表  ns: make([]*Node, LEVEL)
  5. }
  6. // 新建跳表节点
  7. func newNode(v int) *Node {
  8.     return &Node { val: v, ns: make([]*Node, LEVEL) }
  9. }
  10. // 核心:辅助查找函数。
  11. // 作用:找到每一层中最后一个小于 v 的节点。这里需要从上往下找
  12. func (this *Skiplist) find(v int) []*Node {
  13.     pre := make([]*Node, LEVEL)
  14.     p := this.head
  15.     for i := LEVEL - 1; i >= 0; i -- {
  16.         for p.ns[i] != nil && p.ns[i].val < v {
  17.             p = p.ns[i]
  18.         }
  19.         pre[i] = p
  20.     }
  21.     return pre
  22. }
  23. // 跳表结构
  24. type Skiplist struct {
  25.     head *Node   
  26. }
  27. // 初始化跳表头指针,作为虚拟头节点,跳表各层的虚拟头节点
  28. func Constructor() Skiplist {
  29.     return Skiplist{head: newNode(-1)}
  30. }
  31. // 查找
  32. // 1. 辅助函数,先找到每一层中最后一个小于 v 的节点列表
  33. // 2. 第 0 层包含所有元素。pre[0] 代表小于 target 的最后一个元素
  34. // 3. pre[0].ns[0] 则为第零层小于 target 的下一个第 0 层的元素。
  35. // 4. 若其不为 nil 并且值等于 target 时,target 则存在,否则不存在。
  36. func (this *Skiplist) Search(target int) bool {
  37.     pre := this.find(target)
  38.     p := pre[0].ns[0]
  39.     return p != nil && p.val == target
  40. }
  41. // 插入
  42. // 1. 辅助函数,先找到每一层中最后一个小于 v 的节点列表
  43. // 2. 运用链表插入的方式将新建的 num 节点插入进跳表中。
  44. // 3. 注意该节点有 50% 的几率会被加入上层跳表
  45. func (this *Skiplist) Add(num int)  {
  46.     pre := this.find(num)
  47.     p := newNode(num)
  48.     for i := 0; i < LEVEL; i ++ {
  49.         p.ns[i] = pre[i].ns[i]
  50.         pre[i].ns[i] = p
  51.         if rand.Intn(2) == 0 {
  52.             break
  53.         }
  54.     }
  55. }
  56. // 删除
  57. // 1. 辅助函数,先找到每一层中最后一个小于 v 的节点列表
  58. // 2. 先查询一下 num 是否存在, 不存在无法删除,直接返回即可
  59. // 3. 再进行每一层的判断这个元素是否存在,存在则进行单链表的删除即可
  60. func (this *Skiplist) Erase(num int) bool {
  61.     pre := this.find(num)
  62.     if this.Search(num) == false {
  63.         return false
  64.     }
  65.     p := pre[0].ns[0]
  66.     for i := 0; i < LEVEL && pre[i].ns[i] == p; i ++ {
  67.         pre[i].ns[i] = p.ns[i]
  68.     }
  69.     return true
  70. }
  71. /**
  72. * Your Skiplist object will be instantiated and called as such:
  73. * obj := Constructor();
  74. * param_1 := obj.Search(target);
  75. * obj.Add(num);
  76. * param_3 := obj.Erase(num);
  77. */
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表