Redis为什么用跳表实现有序集合

Redis为什么用跳表实现有序集合

手写一个跳表

为了更好的回答上述问题以及更好的理解和掌握跳表,这里可以通过手写一个简单的跳表的形式来帮助读者理解跳表这个数据结构。

我们都知道有序链表在添加、查询、删除的平均时间复杂都都是 O(n) 即线性增长,所以一旦节点数量达到一定体量后其性能表现就会非常差劲。而跳表我们完全可以理解为在原始链表基础上,建立多级索引,通过多级索引检索定位将增删改查的时间复杂度变为 O(log n)

可能这里说的有些抽象,我们举个例子,以下图跳表为例,其原始链表存储按序存储 1-10,有 2 级索引,每级索引的索引个数都是基于下层元素个数的一半。

img

假如我们需要查询元素 6,其工作流程如下:

  1. 从 2 级索引开始,先来到节点 4。

  2. 查看 4 的后继节点,是 8 的 2 级索引,这个值大于 6,说明 2 级索引后续的索引都是大于 6 的,没有再往后搜寻的必要,我们索引向下查找。

  3. 来到 4 的 1 级索引,比对其后继节点为 6,查找结束。

相较于原始有序链表需要 6 次,我们的跳表通过建立多级索引,我们只需两次就直接定位到了目标元素,其查寻的复杂度被直接优化为O(log n)

img

对应的添加也是一个道理,假如我们需要在这个有序集合中添加一个元素 7,那么我们就需要通过跳表找到小于元素 7 的最大值,也就是下图元素 6 的位置,将其插入到元素 6 的后面,让元素 6 的索引指向新插入的节点 7,其工作流程如下:

  1. 从 2 级索引开始定位到了元素 4 的索引。

  2. 查看索引 4 的后继索引为 8,索引向下推进。

  3. 来到 1 级索引,发现索引 4 后继索引为 6,小于插入元素 7,指针推进到索引 6 位置。

  4. 继续比较 6 的后继节点为索引 8,大于元素 7,索引继续向下。

  5. 最终我们来到 6 的原始节点,发现其后继节点为 7,指针没有继续向下的空间,自此我们可知元素 6 就是小于插入元素 7 的最大值,于是便将元素 7 插入。

img

这里我们又面临一个问题,我们是否需要为元素 7 建立索引,索引多高合适?

我们上文提到,理想情况是每一层索引是下一层元素个数的二分之一,假设我们的总共有 16 个元素,对应各级索引元素个数应该是:

1. 一级索引:16/2=8
2. 二级索引:8/2 =4
3. 三级索引:4/2=2

由此我们用数学归纳法可知:

1. 一级索引:16/2=16/2^1=8
2. 二级索引:8/2 => 16/2^2 =4
3. 三级索引:4/2=>16/2^3=2

假设元素个数为 n,那么对应 k 层索引的元素个数 r 计算公式为:

r=n/2^k

同理我们再来推断以下索引的最大高度,一般来说最高级索引的元素个数为 2,我们设元素总个数为 n,索引高度为 h,代入上述公式可得:

2= n/2^h
=> 2*2^h=n
=> 2^(h+1)=n
=> h+1=log2^n
=> h=log2^n -1

而 Redis 又是内存数据库,我们假设元素最大个数是65536,我们把65536代入上述公式可知最大高度为 16。所以我们建议添加一个元素后为其建立的索引高度不超过 16。

因为我们要求尽可能保证每一个上级索引都是下级索引的一半,在实现高度生成算法时,我们可以这样设计:

  1. 跳表的高度计算从原始链表开始,即默认情况下插入的元素的高度为 1,代表没有索引,只有元素节点。

  2. 设计一个为插入元素生成节点索引高度 level 的方法。

  3. 进行一次随机运算,随机数值范围为 0-1 之间。

  4. 如果随机数大于 0.5 则为当前元素添加一级索引,自此我们保证生成一级索引的概率为 50% ,这也就保证了 1 级索引理想情况下只有一半的元素会生成索引。

  5. 同理后续每次随机算法得到的值大于 0.5 时,我们的索引高度就加 1,这样就可以保证节点生成的 2 级索引概率为 25% ,3 级索引为 12.5% ……

我们回过头,上述插入 7 之后,我们通过随机算法得到 2,即要为其建立 1 级索引:

img

最后我们再来说说删除,假设我们这里要删除元素 10,我们必须定位到当前跳表各层元素小于 10 的最大值,索引执行步骤为:

  1. 2 级索引 4 的后继节点为 8,指针推进。

  2. 索引 8 无后继节点,该层无要删除的元素,指针直接向下。

  3. 1 级索引 8 后继节点为 10,说明 1 级索引 8 在进行删除时需要将自己的指针和 1 级索引 10 断开联系,将 10 删除。

  4. 1 级索引完成定位后,指针向下,后继节点为 9,指针推进。

  5. 9 的后继节点为 10,同理需要让其指向 null,将 10 删除。

img

总结:

有几个原因:

1、它们不是很占用内存。这主要取决于你。改变节点拥有给定层数的概率的参数,会使它们比 B 树更节省内存。

2、有序集合经常是许多 ZRANGE 或 ZREVRANGE 操作的目标,也就是说,以链表的方式遍历跳表。通过这种操作,跳表的缓存局部性至少和其他类型的平衡树一样好。

3、它们更容易实现、调试等等。例如,由于跳表的简单性,我收到了一个补丁(已经在 Redis 主分支中),用增强的跳表实现了O(log(N))的 ZRANK。它只需要对代码做很少的修改。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

微服务核心——网关路由

目录 前言 一、登录存在的问题归纳 二、*微服务网关整体方案 三、认识微服务网关 四、网关鉴权实现 五、OpenFeign微服务间用户标识信息传递实现 六、微服务网关知识追问巩固 前言 本篇文章具体讲解微服务中网关的实现逻辑、用于解决什么样的问题。其中标题中标注* 涉…

消息中间件类型介绍

ActiveMQ: ActiveMQ可是个老将了,它功能全面、稳定可靠,还支持多种协议和编程语言。如果你需要一个兼容性好、易于集成的消息中间件,ActiveMQ可是个不错的选择。 RabbitMQ: RabbitMQ以其简单易用和高性能著称。它支持丰…

5G在汽车零部件行业的应用

5G技术在汽车零部件行业的应用正在不断深入,为行业的智能化、自动化和高效化转型提供了强大的技术支持。 1、5G技术特点与优势 5G技术具有高速度、低延迟、大连接和切片技术等特点与优势。这些特性为汽车零部件行业提供了稳定、可靠、高效的通信连接,使…

MySQL【二】

查询列 SELECT [ALL | DISTINCT ] * | 列名1[,……列名n] FROM 表名; 查询所有选课学生的学号,结果去除重复值 select distinct sno from sc; 选择行 查询满足条件的数据集 SELECT 字段列表 FROM 表名 WHERE 查询条件 查询不属于数学系或外国语系的学生全部信息 …

【LLM论文日更】LongReward:利用人工智能反馈改进长上下文大语言模型

论文:https://arxiv.org/pdf/2410.21252代码:https://github.com/THUDM/LongReward机构:清华大学 & 中科院 & 智谱领域:长上下文LLM发表:arxiv 研究背景 研究问题:这篇文章要解决的问题是如何在长…

Windows Terminal终端美化

Windows Terminal 1. 下载: 终端: 直接在微软的store中搜索 windows terminal ,直接获取即可 美化用到的字体:https://www.nerdfonts.com/font-downloads 这里的随便一个都可以,下载解压后,选中所有ttf文…

Go语言基础语法

一、创建工程 说明: (1)go.mod文件是go项目依赖管理文件,相当于前端的package.json,也就是Java项目中的Maven的pom.xml。 二、打印数据到控制台 (1)引入fmt (2)使用fmt…

【数据结构】二叉树——层序遍历

层序遍历 一、层序遍历二、层序遍历(递归)三、层序遍历(非递归)四、总结 一、层序遍历 层序遍历是一种广度优先遍历 以图上二叉树为例,层序遍历就是按照二叉树的深度一层一层进行遍历 遍历顺序: A B C D …

使用DJL和PaddlePaddle的口罩检测详细指南

使用DJL和PaddlePaddle的口罩检测详细指南 完整代码 该项目利用DJL和PaddlePaddle的预训练模型,构建了一个口罩检测应用程序。该应用能够在图片中检测人脸,并将每张人脸分类为“戴口罩”或“未戴口罩”。我们将深入分析代码的每个部分,以便…

Pandas 数据清洗

1.数据清洗定义 数据清洗是对一些没有用的数据进行处理的过程。很多数据集存在数据缺失、数据格式错误、错误数据或重复数据的情况,如果要使数据分析更加准确,就需要对这些没有用的数据进行处理。 2.清洗空值 DataFrame.dropna(axis0, howany, threshN…

【GL08】STM32--ADC/DAC

一、ADC简介 ADC 即模拟信号到数字信号的转换,即用数字信号展现模拟的世界,所有的计算机或者数字处理器只能接受以 0 和 1 两种状态的数字信号,而对于模拟信号,则无法识别,而需要经过模拟数字转换器来感受模拟的世界。…

Blender进阶:着色器节点

11 着色器节点 11.1着色器 着色器Shader,负责给物体表面着色。 综合以下参数: -基础色-金属度、粗超度、透明度-法向-入射光颜色、强度、角度。。 着色器本质上是一段程序、算法,即着色器编程。 在节点编辑器中,支持算法的可…

SQLark百灵连接——整合项目监控过程

关键词:SQL编写、数据查询、数据导入、达梦数据库、项目管理、信息透明 项目监控背景 作为新手项目经理的我,经常觉得哪儿哪儿都是问题,今天催这个,明天推那个,可就是什么事都推不动,谁都不配合。后来&…

ELK配置转存redis缓存,采集nginx访问日志

在136服务器上部署mysql 启动mysql服务 可通过以下命令查找安装的软件包 怎么查找安装软件的日志文件位置rpm -qc mysql-server,即可显示mysql.log位置 也可通过查找配置文件中的log关键字来查找log文件日志位置 用awk命令,以切割,输出第二个…

提升当当网数据爬取效率:代理IP并发抓取技术

在当今的互联网时代,数据已成为企业竞争的关键资源。爬虫技术作为获取网络数据的重要手段,其应用范围越来越广泛。然而,随着各大网站反爬虫机制的不断加强,爬虫面临着越来越多的挑战。其中,IP被封禁是最常见的问题之一…

基于微信小程序的图书馆座位预约系统+LW示例参考

系列文章目录 1.基于SSM的洗衣房管理系统原生微信小程序LW参考示例 2.基于SpringBoot的宠物摄影网站管理系统LW参考示例 3.基于SpringBootVue的企业人事管理系统LW参考示例 4.基于SSM的高校实验室管理系统LW参考示例 5.基于SpringBoot的二手数码回收系统原生微信小程序LW参考示…

学习笔记:ElasticSearch搜索引擎

学习视频:【尚硅谷】ElasticSearch教程入门到精通(基于ELK技术栈elasticsearch 7.x8.x新特性) 学习笔记:Elasticsearch学习笔记 目录 第1章 Elasticsearch概述01. 开篇02. 技术选型 2. 第二章 ElasticSearch入门03. 环境准备04. …

Vue Router进阶详解

导航守卫 若依框架登录鉴权详解(动态路由)_若依鉴权-CSDN博客 完整的导航解析流程 导航被触发: 当用户点击页面中的链接、使用编程式导航(如router.push或router.replace)或手动输入URL时,导航流程被触发。…

力扣排序242题 有效的子母异位词

题目: 242.有效的字母异位词 给定两个字符串s和t ,编写一个函数来判断 t是否是s的字母异位词。 示例1: 输入: s "anagram", t "nagaram" 输出: true 解题思路: 要判断两个字符串s和t是否为子母异位词,也…

html简易流程图

效果图 使用htmlcssjs&#xff0c;无图片&#xff0c;没用Canvas demo: <!DOCTYPE html> <html> <head><link href"draw.css" rel"stylesheet" /><script src"draw.js" type"text/javascript"></…