• 【Leetcode】1206. Design Skiplist


    题目地址:

    https://leetcode.com/problems/design-skiplist/

    实现一个跳表。

    跳表如下图所示:
    在这里插入图片描述
    其是一个分层有序链表结构(有序指的是按元素大小排序,类似于平衡树),设最下层为第 0 0 0层,向上的层数以此类推。每一层的节点数大致是其上面一层的两倍,而查找元素的时候可以通过高层的快速跳跃实现优化。有个虚拟头结点为搜索的出发节点。核心操作为“前驱查找”,这个操作会找到每一层的小于给定元素 x x x的最后一个节点(如果不存在,则视为找到了头结点)。

    每个操作具体如下:
    1、查找 x x x是否存在。先对 x x x进行前驱查找,然后看第 0 0 0层找到的那个节点的下一个节点,只需要看其是否值为 x x x即可;
    2、添加 x x x。先对 x x x进行前驱查找,然后看第 0 0 0层找到的那个节点,如果那个节点存在,则其为小于 x x x的最后一个节点;如果不存在,那个节点即为头结点。无论怎样,都可以在那个节点之后添加新的值为 x x x的节点。此时,以 50 % 50\% 50%的概率将这个节点插到上一层,插完之后再以 50 % 50\% 50%的概率将这个节点插到更上一层,一直插到最上层或中间停止了为止。
    3、删除 x x x。先对 x x x进行前驱查找,然后看第 0 0 0层找到的那个节点,看其下一个节点,如果不存在,或者值不为 x x x,则说明 x x x不存在,直接返回。否则说明找到了,删除之,并向上爬,删除上面层中的 x x x

    代码如下:

    class Skiplist {
     public:
    #define level 8
      struct Node {
        int val;
        vector<Node*> next;
        Node(int val) : val(val) {
          next.resize(level, nullptr);
        }
      } *head;
    
      Skiplist() {
        head = new Node(-1);
      }
    
      ~Skiplist() {
        delete head;
      }
    
      void find(int x, vector<Node*> &pre) {
        // 从头结点出发,找到每一层小于x的最后一个节点,如果没找到则赋为头结点
        auto p = head;
        for (int i = level - 1; i >= 0; i--) {
          while (p->next[i] && p->next[i]->val < x) p = p->next[i];
          pre[i] = p;
        }
      }
    
      bool search(int x) {
        vector<Node*> pre(level);
        find(x, pre);
        auto p = pre[0]->next[0];
        return p && p->val == x;
      }
    
      void add(int x) {
        vector<Node*> pre(level);
        find(x, pre);
        auto p = new Node(x);
        // 从下向上每一层添加节点x
        for (int i = 0; i < level; i++) {
          p->next[i] = pre[i]->next[i];
          pre[i]->next[i] = p;
          // 以50%的概率退出
          if (rand() & 1) break;
        }
      }
    
      bool erase(int x) {
        vector<Node*> pre(level);
        find(x, pre);
        auto p = pre[0]->next[0];
        if (!p || p->val != x) return false;
        for (int i = 0; i < level && pre[i]->next[i] == p; i++)
          pre[i]->next[i] = p->next[i];
        delete p;
        return true;
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    每个操作平均时间复杂度 O ( log ⁡ n ) O(\log n) O(logn) n n n为表里已有的元素个数(即最下面一层的节点个数),空间平均 O ( n ) O(n) O(n)

  • 相关阅读:
    自动驾驶:2022 apollo day 观后感(三)
    【前端】常用属性及实例
    为什么建议使用虚拟机来安装Linux?
    CSS学习笔记05-背景图片
    使用docker创建minio镜像并上传文件,提供demo
    自学前端开发 - VUE 框架 (四) 组合式 API
    uni-app开发小程序实用技巧
    线上线下交友社区系统 可打包小程序 支持二开 源码交付!
    【RabbitMQ】RabbitMQ 消息的堆积问题 —— 使用惰性队列解决消息的堆积问题
    28 mysql 数据记录的 存储更新删除
  • 原文地址:https://blog.csdn.net/qq_46105170/article/details/126093361