1206. 设计跳表

2023-11-04

1206. 设计跳表

不使用任何库函数,设计一个 跳表 。

跳表 是在 O(log(n)) 时间内完成增加、删除、搜索操作的数据结构。跳表相比于树堆与红黑树,其功能与性能相当,并且跳表的代码长度相较下更短,其设计思想与链表相似。

例如,一个跳表包含 [30, 40, 50, 60, 70, 90] ,然后增加 80、45 到跳表中,以下图的方式操作:

跳表中有很多层,每一层是一个短的链表。在第一层的作用下,增加、删除和搜索操作的时间复杂度不超过 O(n)。跳表的每一个操作的平均时间复杂度是 O(log(n)),空间复杂度是 O(n)。

在本题中,你的设计应该要包含这些函数:

bool search(int target) : 返回target是否存在于跳表中。
void add(int num): 插入一个元素到跳表。
bool erase(int num): 在跳表中删除一个值,如果 num 不存在,直接返回false. 如果存在多个 num ,删除其中任意一个即可。
注意,跳表中可能存在多个相同的值,你的代码需要处理这种情况。

跳表结点 SkipNode

struct SkipNode {
    int val;
    vector<SkipNode*> next;
    SkipNode(int val, int level) :val(val), next(level, nullptr) {};
};

跳表 Skiplist 

class Skiplist {
private:
    SkipNode* head;//头结点(初始化为最高层数) 
    int level;//当前跳表的最高层数(除了头结点) 
public:
    bool search(int target)
    {
        从当前最高层开始遍历,如果当前结点的下一个结点大于target(或者为nullptr),
        则进入当前结点的下一层,继续向右遍历。
    }
    void add(int num)
    {
        创建一个新结点,给它随机一个层数newlevel,然后更新当前最高层数;
        接着从当前最高层开始遍历,如果当前结点的下一个结点大于target(或者为nullptr),
        则让新结点指向当前结点的下一个结点,当前结点指向新结点
        注意:自顶向下,从newlevel ~ 0都要连一次
    }
    bool erase(int num)
    {
        从当前最高层开始遍历,如果当前结点的下一个结点大于target(或者为nullptr),
        则进入当前结点的下一层,继续向右遍历。
        如果当前结点的下一个结点等于target,则让当前结点指向下一个的下一个结点,完成删除
        注意:自顶向下,要删的结点,每一层都要删一次

        删除完成之后,记得更新当前跳表的最高层数       
    }
};

实现代码: 

#include <bits/stdc++.h>
using namespace std;
const int MAX_LEVEL = 8;//最高8层 

struct SkipNode {
    int val;
    vector<SkipNode*> next;
    SkipNode(int val, int level) :val(val), next(level, nullptr) {};
};

class Skiplist {
private:
    SkipNode* head;//头结点(初始化为最高层数) 
    int level;//当前跳表的最高层数(除了头结点) 
public:
    Skiplist() :head(new SkipNode(-1, MAX_LEVEL)), level(0) {}
    int randomLevel()//插入一个新结点时,随机它的层数 
    {
        int rlevel = 1;
        while (rand() % 2 == 0 && rlevel < MAX_LEVEL)//如果随机出 0,层数+1,如果随机出 1,结束递增 
        {
            rlevel++;
        }
        return rlevel;
    }
    //查找 
    bool search(int target)
    {
        SkipNode* cur = head;
        for (int i = level - 1; i >= 0; i--)//从最高层开始 
        {
            while (cur->next[i] != nullptr && cur->next[i]->val < target)//向右遍历,直至当前结点的下一个结点 >= target
            {
                cur = cur->next[i];
            }
            if (cur->next[i] != nullptr && cur->next[i]->val == target)
            {
                return true;
            }
            //如果当前结点的下一个结点 > target,则进入下一层 
        }
        return false;
    }
    //添加 
    void add(int num)
    {
        int newlevel = randomLevel();//随机一个层数 
        if (newlevel > level)//更新 level 
        {
            level = newlevel;
        }
        SkipNode* newNode = new SkipNode(num, newlevel);
        SkipNode* cur = head;
        for (int i = level - 1; i >= 0; i--)
        {
            while (cur->next[i] != nullptr && cur->next[i]->val < num)
            {
                cur = cur->next[i];
            }
            if (i < newlevel)//插入一个新增结点 
            {
                newNode->next[i] = cur->next[i];
                cur->next[i] = newNode;
            }
        }
    }
    bool erase(int num)
    {
        SkipNode* cur = head;
        SkipNode* toDelete = nullptr;
        for (int i = level - 1; i >= 0; i--)
        {
            while (cur->next[i] != nullptr && cur->next[i]->val < num)
            {
                cur = cur->next[i];
            }
            if (cur->next[i] != nullptr && cur->next[i]->val == num)
            {
                toDelete = cur->next[i];
                cur->next[i] = toDelete->next[i];//在 当前这一层 上删除目标结点 
            }
        }
        //更新 level 
        while (level >= 1 && head->next[level - 1] == nullptr) {
            level--;
        }
        //delete 防止内存泄露 
        if (toDelete != nullptr) {
            delete toDelete;
            return true;
        }
        else {
            return false;
        }
    }
};

int main()
{
    Skiplist* skiplist = new Skiplist();
    skiplist->add(1);
    skiplist->add(2);
    skiplist->add(3);
    cout << skiplist->search(0) << endl;   // 返回 0
    skiplist->add(4);
    cout << skiplist->search(1) << endl;   // 返回 1
    cout << skiplist->erase(0) << endl;    // 返回 0
    cout << skiplist->erase(1) << endl;    // 返回 1
    cout << skiplist->search(1) << endl;   // 返回 0
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

1206. 设计跳表 的相关文章

  • 如何从 python 将无穷大传递给 redis?

    我正在使用 redis py 并希望将 inf 和 inf 与 ZRANGEBYSCORE 一起使用 我尝试使用 inf 的字符串和浮点来执行此操作 但它们返回一个空集 我怎样才能做到这一点 EDIT 我尝试执行以下命令 redis Str
  • python 3.5 中的 json.loads 和 Redis

    我使用 json dumps 创建了一个 JSON 对象 并在 Redis 列表中将其 RPUSH ed 当使用 LRANGE redis lrange 返回 JSON 时 我收到一个二进制字符串 b si 00 ff 所以 json lo
  • 我的 Redis 自动生成的密钥

    我不知道我的 Redis 版本 4 0 9 到底发生了什么 我正在运行一个应用程序并使用 Redis 来存储我的数据库 但是 然后 Redis 自动创建 3 个新键 Backup1 Backup2 Backup3 并删除我的所有数据 这是我
  • 如何设置 Celery 以通过 ssl 与 Azure Redis 实例对话

    使用 的伟大答案 如何在microsoft azure上的django项目中配置celery redis https stackoverflow com questions 39616701 how to configure celery
  • Redis 排序集和解决关系

    我正在使用 Redis 排序集来存储我正在处理的项目的排名 我们没有预料到 我们想要如何处理关系 Redis 按字典顺序对具有相同分数的条目进行排序 但我们想要做的是对具有相同分数的所有条目给予相同的排名 例如在以下情况 redis 127
  • 从redis中检索大数据集

    一台服务器上的应用程序查询另一台服务器上运行的 Redis 查询的结果数据集约为 250kzrangebyscore objects locations inf inf这在应用程序服务器上似乎需要 40 秒 当使用命令执行时redis cl
  • 如何批量删除Redis中数十万个带有特殊字符的key

    我们有一个包含数十万个 Redis 键的列表 其中包含各种特殊字符 我们希望批量删除它们 对于这个问题上的类似问题 有一些很好的答案 如何使用 Redis 自动删除与模式匹配的键 https stackoverflow com questi
  • 在 aws-elasticache 上使用 memcached 或 Redis

    我正在 AWS 上开发一个应用程序 并使用 AWS elasticache 进行缓存 我对使用 memcached 或 redis 感到困惑 我阅读了有关 redis 3 0 2 更新以及它现在如何等同于 memchached 的文章 ht
  • 如何将node.js管道传输到redis?

    我有很多数据要插入 SET INCR 到redis DB 所以我正在寻找pipeline http redis io topics pipelining 质量插入 http redis io topics mass insert通过node
  • 无法启动redis.service:单元redis-server.service被屏蔽

    我在 ubuntu 16 04 上安装了 Redis 服务器 但是当我尝试使用启动redis服务时 sudo systemctl start redis 我收到消息 Failed to start redis service Unit re
  • Redis Cluster 与 Pub/Sub 中的 ZeroMQ,用于水平扩展的分布式系统

    如果我要设计一个巨大的分布式系统 其吞吐量应随系统中的订阅者数量和通道数量线性扩展 哪个会更好 1 Redis集群 仅适用于Redis 3 0 alpha 如果是集群模式 您可以在一个节点上发布并在另一个完全不同的节点上订阅 消息将传播并到
  • 使用 Sentinels 升级 Redis 的最佳实践?

    我有 3 个 Redis 节点 由 3 个哨兵监视 我进行了搜索 文档似乎不清楚如何最好地升级此类配置 我目前使用的是 3 0 6 版本 我想升级到最新的 5 0 5 我对这方面的程序有几个疑问 升级两个大版本可以吗 我在我们的暂存环境中执
  • 为什么 Redis TimeSeries 不捕获聚合中的最后一个元素?

    我试图了解 Redis 的时间序列规则创建的工作原理 但我很困惑为什么 Redis 会忽略聚合中的最后一项 并想知道这是否是预期的行为 我在中创建了示例代码redis cli为了显示 127 0 0 1 6379 gt FLUSHALL O
  • Java 将字节转换为二进制安全字符串

    我有一些以字节为单位的数据 我想将它们放入Redis中 但是Redis只接受二进制安全字符串 而我的数据有一些二进制非安全字节 那么如何将这些字节转换为二进制安全字符串以便将它们保存到 Redis 中呢 Base64 对我有用 但它使数据更
  • Redis、会话过期和反向查找

    我目前正在构建一个网络应用程序 并想使用 Redis 来存储会话 登录时 会话会使用相应的用户 ID 插入到 Redis 中 并且过期时间设置为 15 分钟 我现在想实现会话的反向查找 获取具有特定用户 ID 的会话 这里的问题是 由于我无
  • 有没有办法用Lettuce自动发现Redis集群中新的集群节点IP

    我有一个Redis集群 3主3从 运行在一个库伯内斯簇 该集群通过Kubernetes 服务 Kube 服务 我将我的应用程序服务器连接到 Redis 集群 使用Kube 服务作为 URI 通过 Redis 的 Lettuce java 客
  • redis - 使用哈希

    我正在使用 redis 为我的 Web 应用程序实现社交流和通知系统 我是 redis 的新手 我对哈希值及其效率有一些疑问 我读过这篇很棒的文章Instagram 帖子 http instagram engineering tumblr
  • 如何使 Redis 缓存中数据层次结构(树)的部分内容无效

    我有一些产品数据 需要在 Redis 缓存中存储多个版本 数据由 JSON 序列化对象组成 获取普通 基本 数据的过程很昂贵 将其定制为不同版本的过程也很昂贵 因此我想缓存所有版本以尽可能进行优化 数据结构看起来像这样 BaseProduc
  • redis dump.rdb / 保存小文件

    Context 我正在使用redis 数据库小于 100 MB 但是 我想进行每日备份 我也在 Ubuntu Server 12 04 上运行 当输入 redis cli save 我不知道 dump rdb 保存到哪里 因为 redis
  • 创建 C++ Redis 模块 - “不导出 RedisModule_OnLoad() 符号”

    我在加载 Redis 模块时遇到一些问题 我只是复制来自的示例https redis io topics modules intro https redis io topics modules intro 但我把它剥下来了 include

随机推荐

  • python读取word报错

    python读取word报错 一定要检查有没有word占用 错误窗口是否关闭 否则会报错缺少哪个模块 其实是中断了读取的操作 半小时找bug 结果是没关掉word报错窗口
  • Redis主从复制出现错误:master_link_status:down

    因为主机设置了密码 我的解决方案是切换到主机redis config中 注销密码 最后重新启动80端口 美滋滋又能的变成up了 root localhost redis 7 0 2 src redis server redis80 conf
  • lua判断字符不为空或空格_Lua - 空值判断的几种情况

    小宅按 在安全领域 lua编程语言因为其小巧在众多工具上都作为插件开发语言 常见的有openresty nmap等 因此笔者将会开辟一个Lua相关的系列文章 主要记录工作过程中一些领悟或者是一些踩过的坑 希望能够借此平台帮助到读者们 0x0
  • 上限、下限、上上限和下下限都是什么区别!

    http bbs gkong com archive aspx id 437896
  • QT5.9.8 update()源码剖析

    1 update调用 在QT中 所有的GUI最终都继承自QWidget 因此所的调用update 都是基类QWidget的update 在QWidget中 路径 Qt Qt5 9 8 5 9 8 Src qtbase src widgets
  • 蓝牙mesh组网-JDY-24M初步探索

    操作步骤如下 这款JDY 24M蓝牙功能强大 我主要应用其中mesh组网这个功能 mesh组网简单来说 就是组网的这几个蓝牙是可以互相通信 一一通信是通过蓝牙地址来确定的 一 配置组网 需要用到两根USB转TTL的线 JDY 24M蓝牙2个
  • LaTex目录管理

    LaTex生成章节 图片 表格目录 章节目录 在latex中 每个章节都由特定的关键字命令定义 如 section subsection subsubsection 等 利用这些关键字 我们可以生成文章的章节结构 并根据这些章节的结构和标题
  • 抖音爆火李峋同款爱心代码,简单附带教程,还有烟花代码,手残党也能学会!!

    最近看到抖音爆火的一些HTML代码 有人找 极客G 更新 今天用了几个小时给大家整理出来了下面几个代码 最简单的就是第一个爱心代码 第二个烟花代码可自定义文本 具体看下面 代码是HTML语言 前面是使用教程 只需要代码的请划到下方进行下载
  • Linux基础——运维 (operation)

    1 什么是运维 1 1 技术人员之间 会对运维有个开玩笑的认知 运维就是修电脑的 装网线的其实不然 运维是一个非常广泛的定义 1 2 基础运维 申请域名 购买 租用服务器 上架 调整网络设备的设置 部署操作系统和运行环境 部署代码 设计和部
  • 数学建模——Matlab中rem与mod区别

    求余函数和求模函数有相同的地方但又不完全一致 主要的区别在于对负整数进行除法运算的操作不同 对于整数a b来说 求余运算或求模运算的方法都是先求整数商c a b 再求余数或模r a c b 求余运算在取c的值时 向0方向取整 fix函数 而
  • 985的分数,却毅然选择了普本

    这两天看到一个问题 如果分数只是擦边进985211院校 那是保住985211的学历还是选普通本科大学自己喜欢的专业读 今天来聊一下我的看法 首先针对这个问题说一下我的看法 能够进入985 就不要选择211 能够进入211就不要选择普通一本
  • VirtualBox安装出现严重错误

    H3C是我们学习很好用的软件 H3C虚拟平台的运行需要VirtualBox虚拟机之上 简单来说 要想使用H3C就必须要正确安装VirtualBox 如果有的小伙伴在卸载VirtualBox的时候 卸载方式不得当 导致VirtualBox残余
  • JNI调用native方法出现 java.lang.UnsatisfiedLinkError: XXXclass.XXXmethod()异常的解决办法

    JNI调用native方法出现 java lang UnsatisfiedLinkError XXXclass XXXmethod 异常的解决办法 参考文章 1 JNI调用native方法出现 java lang UnsatisfiedLi
  • Servlet——文件上传

    文件上传 文章目录 文件上传 1 Form表单形式实现 1 1 前端 1 2 后端 1 3 实现文件的上传然后保存到本地 2 Js Ajax实现 1 Form表单形式实现 1 1 前端 更改表单提交方式 form enctype multi
  • 功能测试在软件开发周期中的作用是什么?

    功能测试是软件开发周期中不可或缺的一个环节 其作用在于保证软件交付给用户之后满足用户需求和预期 在本文中 我们将详细解析软件开发周期中功能测试的作用 首先 功能测试是软件开发周期中质量保证的重要环节 在开发阶段 开发人员会编写代码 并使用不
  • 技术岗/算法岗面试如何准备?5000字长文、6个角度以2023秋招经历分享面试经验

    技术岗 算法岗面试流程是什么样的 技术面都干什么 Coding 机试如何准备 技术面考察哪些知识 如何准备 项目八股如何准备 简历要注意什么 怎么做 大家好 我是卷了又没卷 薛定谔的卷的大厂算法工程师 陈城南 本文会从以上6个问题 全方位
  • jdbc处理时间问题

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 遇到的问题如下 数据库中对应的字段属性为TIMESTAMP 6 java中类属性对应的字段为java util Date 虽然数据库中保存的是 2014 05 12 10
  • 一文带你精通Burp(附下载)

    添加链接描述 一文带你精通Burp 附下载
  • spring @EventListener 事件与监听

    1 自定义Application Event public class MyEvent extends ApplicationEvent private static final long serialVersionUID 1L priva
  • 1206. 设计跳表

    1206 设计跳表 不使用任何库函数 设计一个 跳表 跳表 是在 O log n 时间内完成增加 删除 搜索操作的数据结构 跳表相比于树堆与红黑树 其功能与性能相当 并且跳表的代码长度相较下更短 其设计思想与链表相似 例如 一个跳表包含 3