链表(4) ----跳表

跳表(Skip List)是一种随机化的数据结构,用于替代平衡树(如 AVL 树或红黑树)。它是基于多层链表的,每一层都是上一层的子集。跳表可以提供与平衡树相似的搜索性能,即在最坏情况下,搜索、插入和删除操作都可以在 O(log n) 的时间复杂度内完成。

跳表的基本组成:

  1. 多层链表:跳表由若干层链表组成,每一层都是下面一层的“快速通道”。最底层是基础链表,包含所有的元素。
  2. 索引节点:每一层的链表都包含一些索引节点,这些节点指向下一层的某些节点。
  3. 随机化:每个节点都有相同的概率(通常为 1/2)决定是否向上增加一层,从而创建一个索引节点。

跳表的操作:

  • 搜索:在跳表中搜索元素时,从顶层开始,逐层向下,直到找到元素或到达底层。在每一层,通过索引节点快速跳过多个节点。
  • 插入:插入操作首先在底层进行标准的链表插入。然后,根据随机化过程决定是否在更高层创建索引节点。
  • 删除:删除操作首先在所有包含该元素的层上找到它,然后逐层删除。如果某个层的索引节点在删除操作后变得无效(即前后节点相同),则该索引节点也会被删除。

跳表的优点:

  • 简单性:跳表的实现相对简单,不需要复杂的旋转操作,如平衡树所需的。
  • 性能:跳表提供了与平衡树相似的搜索性能,且在某些情况下,由于其随机化的特性,可能具有更好的性能。
  • 并发操作:跳表适合进行并发操作,因为它的插入和删除操作不需要像平衡树那样进行大量的结构调整。

跳表的应用:

跳表在许多场景下都有应用,尤其是在需要快速搜索、插入和删除操作的数据库和索引系统中。例如,Redis 这个流行的键值存储数据库就使用了跳表来实现有序集合。

跳表是一种非常实用的数据结构,它结合了链表的简单性和平衡树的高效搜索性能。

算法设计:

跳表的概念 

链表的优点 

跳表的设计 

跳表中 的前驱 

跳表的添加 

跳表的删除 

力扣1206  ---跳表 

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

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

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

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

了解更多 : 跳表 - OI Wiki

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

  • bool search(int target) : 返回target是否存在于跳表中。
  • void add(int num): 插入一个元素到跳表。
  • bool erase(int num): 在跳表中删除一个值,如果 num 不存在,直接返回false. 如果存在多个 num ,删除其中任意一个即可。

注意,跳表中可能存在多个相同的值,你的代码需要处理这种情况。

示例 1:

输入
["Skiplist", "add", "add", "add", "search", "add", "search", "erase", "erase", "search"]
[[], [1], [2], [3], [0], [4], [1], [0], [1], [1]]
输出
[null, null, null, null, false, null, true, false, true, false]解释
Skiplist skiplist = new Skiplist();
skiplist.add(1);
skiplist.add(2);
skiplist.add(3);
skiplist.search(0);   // 返回 false
skiplist.add(4);
skiplist.search(1);   // 返回 true
skiplist.erase(0);    // 返回 false,0 不在跳表中
skiplist.erase(1);    // 返回 true
skiplist.search(1);   // 返回 false,1 已被擦除

提示:

  • 0 <= num, target <= 2 * 104
  • 调用searchadd,  erase操作次数不大于 5 * 104 

代码 

MAX_LEVEL = 32
P_FACTOR = 0.5def random_level() -> int:lv = 1while lv < MAX_LEVEL and random.random() < P_FACTOR:lv += 1return lvclass SkiplistNode:__slots__ = 'val', 'forward'def __init__(self, val: int, max_level=MAX_LEVEL):self.val = valself.forward = [None] * max_levelclass Skiplist:def __init__(self):self.head = SkiplistNode(-1)self.level = 0def search(self, target: int) -> bool:curr = self.headfor i in range(self.level - 1, -1, -1):# 找到第 i 层小于且最接近 target 的元素while curr.forward[i] and curr.forward[i].val < target:curr = curr.forward[i]curr = curr.forward[0]# 检测当前元素的值是否等于 targetreturn curr is not None and curr.val == targetdef add(self, num: int) -> None:update = [self.head] * MAX_LEVELcurr = self.headfor i in range(self.level - 1, -1, -1):# 找到第 i 层小于且最接近 num 的元素while curr.forward[i] and curr.forward[i].val < num:curr = curr.forward[i]update[i] = currlv = random_level()self.level = max(self.level, lv)new_node = SkiplistNode(num, lv)for i in range(lv):# 对第 i 层的状态进行更新,将当前元素的 forward 指向新的节点new_node.forward[i] = update[i].forward[i]update[i].forward[i] = new_nodedef erase(self, num: int) -> bool:update = [None] * MAX_LEVELcurr = self.headfor i in range(self.level - 1, -1, -1):# 找到第 i 层小于且最接近 num 的元素while curr.forward[i] and curr.forward[i].val < num:curr = curr.forward[i]update[i] = currcurr = curr.forward[0]if curr is None or curr.val != num:  # 值不存在return Falsefor i in range(self.level):if update[i].forward[i] != curr:break# 对第 i 层的状态进行更新,将 forward 指向被删除节点的下一跳update[i].forward[i] = curr.forward[i]# 更新当前的 levelwhile self.level > 1 and self.head.forward[self.level - 1] is None:self.level -= 1return True

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/47993.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

zlgcan,周立功Can设备,Qt中间件,QtCanBus插件,即插即用

新增zlgcan插件&#xff0c;需要请看下方视频回复联系&#xff01; 视频链接地址&#xff1a; Qt,canbus manager,周立功,zlgcan插件演示,需要请留言_哔哩哔哩_bilibili

反爬虫策略中的IP地址轮换如何实现?挑战与对策

当今互联网时代&#xff0c;各类网站、网络平台背后隐藏着大量数据&#xff0c;广告数据收集、市场数据收集都需要依托爬虫技术&#xff0c;但很多网站通过反爬虫技术限制或屏蔽爬虫的访问&#xff0c;这给数据收集带来不小的挑战。 为了规避这些反爬虫策略&#xff0c;开发人…

JVM--内存分配与回收策略

1.前言 对象的内存分配&#xff0c;从概念上讲&#xff0c;应该都是在堆上分配&#xff08;而实际上也有可能经过即时编译后被拆散 为标量类型并间接地在栈上分配 &#xff09;。在经典分代的设计下&#xff0c;新生对象通常会分配在新生代中&#xff0c;少数 情况下&#xff0…

计数,桶与基数排序

目录 一. 计数排序 概念 步骤思路如下 实现代码如下 时间复杂度与空间复杂度 1. 时间复杂度 2. 空间复杂度 计数排序的特点 二. 桶排序 概念 步骤思路如下 实现代码如下 时间复杂度与空间复杂度 1. 时间复杂度 2. 空间复杂度 桶排序的特点 三. 基数排序 概念 步…

Java核心API——Collection类

Collection类简介 * java集合框架 * java.util.Collection接口&#xff0c;是所有集合的顶级接口&#xff0c;规定了集合所必须的功能 * <p> * 集合与数组一样&#xff0c;可以保存一组具有相同类型元素数据结构 * 并且提供了对于元素的维护操作(方法)。 * 集合有多种不…

千万罚单,稠州商业银行屡教不改?

撰稿|芋圆 来源|贝多财经 今年&#xff0c;浙江稠州商业银行&#xff08;以下简称“稠州商行”&#xff09;似乎进入了多事之秋&#xff0c;刚刚兼并两家经营不善的村镇银行就紧接着收到大额罚单。 该行在2023年的经营业绩不算难看。据2023年年报&#xff0c;稠州商行的业绩从…

PHP 调用 1688 详情 API 接口的实战攻略

在电商领域&#xff0c;获取准确和详细的商品信息对于业务的发展至关重要。1688 作为国内知名的批发采购平台&#xff0c;其详情 API 接口为开发者提供了丰富的数据资源。本文将为您详细介绍如何使用 PHP 调用 1688 详情 API 接口。 一、前期准备 注册 1688 开放平台账号&#…

L2TP(Client-initiated模式)over IPSEC远程拨号实验

一、实验目的及拓扑 实验目的&#xff1a;通过L2TP客户端与LNS服务端建立L2TP隧道并承载在IPSEC网络上。其中L2TPoverIPsec客户端采用windows软终端模式&#xff08;Cloud3&#xff09;&#xff0c;AR1上将内网LNS&#xff08;FW1&#xff09;服务器采用NAT方式向外网进行映射…

基于深度学习的水果识别系统

1. 引言 随着计算机视觉技术的快速发展&#xff0c;基于深度学习的水果识别系统在农业、超市等场景中具有广泛的应用前景。例如&#xff0c;在农业中&#xff0c;水果识别系统可以帮助农民自动分类和计数水果&#xff0c;从而提高效率并减少人工成本&#xff1b;在超市中&…

【机器学习】使用Python的dlib库实现人脸识别技术

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、引言二、传统人脸识别技术1. 基于几何特征的方法2. 基于模板匹配的方法3. 基于统计学习的方法 三、深度学习在脸识别中的应用1. 卷积神经网络&#xff08;CNN&#xff09;2. FaceNet和ArcFace 四、使用Python和dlib库实…

Spring1(开发工具安装及配置 初始Spring 解耦实现 SpringIOC SpringDI Spring常见面试题)

目录 一 、开发工具安装及配置 IDEA简介 安装 配置 常⽤快捷键 部署maven 1.配置环境​编辑 2.创建一个maven项目​编辑 选择maven​编辑​编辑 二、初始Spring Spring历史由来 Spring体系结构 Spring生态系统 三、解耦实现 jdbc 三层思想​编辑 四…

leetcode_26. 删除有序数组中的重复项

leetcode_26. 删除有序数组中的重复项 leetcode链接 题目描述 给你一个 非严格递增排列 的数组 nums &#xff0c;请你** 原地** 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums…

可视化剪辑,账号矩阵,视频分发,聚合私信一体化营销工具 源----代码开发部署方案

可视化剪辑&#xff1a; 为了实现可视化剪辑功能&#xff0c;可以使用流行的视频编辑软件或者开发自己的视频编辑工具。其中&#xff0c;通过设计用户友好的界面&#xff0c;用户可以简单地拖拽和放大缩小视频片段&#xff0c;剪辑出满足需求的视频。在开发过程中&#xff0c;可…

SQL进阶

目录 SQL索引 SQL索引 索引 作用帮助数据库管理系统高效获取数据的数据结构 数据处理分类 OLTP&#xff08;联机事务处理&#xff09;OLAP&#xff08;联机分析处理&#xff09;一般用于处理客户的事务和进行查询一般用于市场的数据分析&#xff08;数据量通常很大&#xff0…

JDK版本详解

Oracle JDK 和 OpenJDK 的对比 Oracle JDK版本将每三年发布一次&#xff0c;而OpenJDK版本每三个月发布一次&#xff1b;OpenJDK 是一个参考模型并且是完全开源的&#xff0c;而Oracle JDK是OpenJDK的一个实现&#xff0c;并不是完全开源的&#xff1b;Oracle JDK 比 OpenJDK …

多源字段聚合重塑算法

要求如下 [[{"oone": "评估是否聘请第三方机构","otwo": null,"othree": "test",},{"oone": "评估是否聘请第三方机构","otwo": null,"othree": "test",}],[{"oon…

python爬虫获取网易云音乐评论歌词以及歌曲地址

python爬虫获取网易云音乐评论歌词以及歌曲地址 一.寻找数据接口二.对负载分析三.寻找参数加密过程1.首先找到评论的请求包并找到发起程序2.寻找js加密的代码 四.扣取js的加密源码1.加密函数参数分析①.JSON.stringify(i0x)②bse6Y(["流泪", "强"])③bse6Y…

探索元宇宙:开启数字世界的奇妙之旅【小学生也能读懂】

元宇宙&#xff1a;数字新纪元的曙光 随着技术的飞速发展&#xff0c;我们正站在一个全新的数字时代的门槛上。元宇宙&#xff08;Metaverse&#xff09;&#xff0c;这个听起来充满未来感的词汇&#xff0c;已经成为科技界的热门话题。它不仅仅是一个概念&#xff0c;更是一个…

C语言——字符串两种声明初始化方式、输入输出及常见处理函数

C语言中的字符串是一个字符数组&#xff0c;它以空字符&#xff08;\0&#xff09;作为结束标志。这意味着字符串中的字符是连续存储在内存中的&#xff0c;而字符串的末尾会自动添加一个\0字符来表示字符串的结束。这种表示方式使得C语言在处理字符串时非常灵活&#xff0c;但…

第1关 -- Linux 基础知识

闯关任务 完成SSH连接与端口映射并运行hello_world.py ​​​​ 可选任务 1 将Linux基础命令在开发机上完成一遍 可选任务 2 使用 VSCODE 远程连接开发机并创建一个conda环境 创建新的虚拟环境lm3 可选任务 3 创建并运行test.sh文件 参考文档 文档&#xff1a;https://g…