跳表数据结构

跳表(Skip List)是一种支持高效插入、删除和查找链表结构,用于加速查找操作,特别适用于有序数据集合。它在Redis、LevelDB等系统中被用于**有序集合(Sorted Set)**的实现。

1. 跳表的结构

跳表的核心思想是:
在链表的基础上,引入多级索引,类似于高速公路的匝道,提高查找效率。

一个跳表通常由多层索引构成:

  • 最底层(Level 0) 是一个有序单链表,存储所有数据;
  • 上层索引(Level > 0)部分节点构成,起到“跳跃”的作用,加速查找;
  • 最上层索引少量节点构成,使得查找可以迅速缩小范围。

示例跳表(3 层):

Level 2:    1 --------------> 9 ----------------> 19
Level 1:    1 ----> 5 -----> 9 ----> 13 -------> 19
Level 0:    1 -> 3 -> 5 -> 7 -> 9 -> 11 -> 13 -> 15 -> 17 -> 19

每一层都是上一层的子集,节点的“晋升”是概率性的(通常是 1/2)。

2. 跳表的基本操作

查找

跳表的查找过程类似于二分查找:

  1. 从最高层索引开始,查找小于目标值的最大节点;
  2. 向下移动到下一层索引,继续查找;
  3. 直到底层(Level 0),最终在普通链表中找到目标节点或确认其不存在。

时间复杂度:
O ( log ⁡ n ) O(\log n) O(logn) —— 因为跳表的高度是O(log n),查找路径长度也大约是O(log n)

插入

  1. 查找插入位置,找到前驱节点;
  2. 随机决定新节点的层数(常见做法:掷硬币,每次有 50% 概率提升一层);
  3. 在每一层进行插入,更新索引。

时间复杂度:

  • 平均 O(log n),因为最多遍历 log n 层;
  • 最坏 O(n)(所有元素都在同一层)。

删除

  1. 查找目标节点(与查找操作类似);
  2. 在每一层移除节点
  3. 如果某层为空,则删除该层

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

3. 跳表 vs. 其他数据结构

数据结构查找时间复杂度插入时间复杂度删除时间复杂度额外空间适用场景
跳表O(log n)O(log n)O(log n)O(n)有序数据、高效查找
平衡二叉搜索树 (AVL, 红黑树)O(log n)O(log n)O(log n)O(n)适用于动态数据
哈希表O(1)O(1)O(1)O(n)适用于无序数据、频繁查询
链表O(n)O(1)O(n)O(n)适用于插入、删除较多

跳表 vs. 红黑树

  • 跳表实现更简单,只需维护索引,而红黑树需要复杂的旋转操作;
  • 跳表在分布式环境中更友好,如 Redis 的有序集合(ZSet)采用跳表;
  • 红黑树适用于内存受限的场景,因为跳表的索引层需要额外存储空间。

4. 实际应用

Redis

Redis 有序集合(Sorted Set) 的底层结构是 跳表 + 哈希表

  • 跳表:用于范围查询ZRANGEZREVRANGE
  • 哈希表:用于O(1) 查找ZADD

LevelDB

Google 的 LevelDB 用跳表存储MemTable,然后刷盘成 SSTable

5. 代码实现(Java)

跳表节点

class SkipListNode {int val;SkipListNode[] next; // 指向不同层级的下一个节点public SkipListNode(int val, int level) {this.val = val;this.next = new SkipListNode[level];}
}

跳表类

import java.util.Random;class SkipList {private static final int MAX_LEVEL = 16;  // 最大层数private final SkipListNode head; // 头节点private int levelCount = 1; // 当前最大层数private final Random random = new Random();public SkipList() {head = new SkipListNode(-1, MAX_LEVEL); // 初始化头节点}// 查找public boolean search(int target) {SkipListNode cur = head;for (int i = levelCount - 1; i >= 0; i--) {while (cur.next[i] != null && cur.next[i].val < target) {cur = cur.next[i];}}cur = cur.next[0];  // 进入最底层return cur != null && cur.val == target;}// 插入public void insert(int num) {SkipListNode[] update = new SkipListNode[MAX_LEVEL]; // 记录每层的前驱节点SkipListNode cur = head;for (int i = levelCount - 1; i >= 0; i--) {while (cur.next[i] != null && cur.next[i].val < num) {cur = cur.next[i];}update[i] = cur;  // 记录前驱节点}int newLevel = randomLevel();levelCount = Math.max(levelCount, newLevel);SkipListNode newNode = new SkipListNode(num, newLevel);for (int i = 0; i < newLevel; i++) {newNode.next[i] = update[i].next[i];update[i].next[i] = newNode;}}// 删除public void delete(int num) {SkipListNode cur = head;SkipListNode[] update = new SkipListNode[MAX_LEVEL];for (int i = levelCount - 1; i >= 0; i--) {while (cur.next[i] != null && cur.next[i].val < num) {cur = cur.next[i];}update[i] = cur;}SkipListNode target = cur.next[0];if (target == null || target.val != num) return;for (int i = 0; i < levelCount; i++) {if (update[i].next[i] != target) break;update[i].next[i] = target.next[i];}}// 随机生成层数private int randomLevel() {int level = 1;while (random.nextDouble() < 0.5 && level < MAX_LEVEL) {level++;}return level;}
}

6. 总结

  • 跳表通过多层索引加速查询,时间复杂度接近O(log n)
  • 插入/删除时动态调整索引,比红黑树实现简单;
  • Redis、LevelDB 等系统采用跳表,适用于有序集合、范围查询等场景;
  • 比红黑树占用更多空间,但更适合并发和分布式环境

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

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

相关文章

系统会把原先的对话状态堆栈从 [“assistant“] 更新为 [“assistant“, “update_flight“]这个更新的处理过程

这个更新主要是在 State 定义中通过 Annotated 来自动处理的。在 State 类型中&#xff0c;我们对 dialog_state 字段绑定了 update_dialog_stack 函数&#xff0c;如下所示&#xff1a; class State(TypedDict):messages: Annotated[list[AnyMessage], add_messages]user_inf…

HTTP发送POST请求的两种方式

1、json String json HttpRequest.post(getUrl(method, "v1", url, userId, appKey)).header("Content-type", "application/json") // 设置请求头为 JSON 格式.body(JSONUtil.toJsonStr(params)) // 请求体为 JSON 字符串.execute().body(); …

Windows 万兴恢复专家 Wondershare Recoverit-v13.5.7.9-[电脑数据恢复工具]

Windows 万兴恢复专家Wondershare_Recoverit 链接&#xff1a;https://pan.xunlei.com/s/VOL3z608vzAj_IYTvH-F1q7kA1?pwdiu89# 1. 打开Setup.exe进行安装&#xff0c;安装完不要打开软件&#xff0c;记住安装目录 2. 将"Crack"文件夹内的所有文件复制到安装目录 …

Blender UV纹理贴图,导出FBX到Unity

加载ps好的模型贴图。右下角选择《材质》基础色里面选择《图像纹理》&#xff0c;选择你的图片。 选择上面UV选项卡。左上角选择UV编辑器。选中物体&#xff0c;TAB进入编辑模式。即可调整映射的图像范围。 其中渲染设置可以在左侧下边脱出。 导出带纹理FBX模型 路径选择复…

华为hcia——Datacom实验指南——以太网帧和IPV4数据包格式(一)

实验开始 第一步配置环境 第二步配置客户端 如图所示&#xff0c;我们把客户端的ip配置成192.168.1.10&#xff0c;网关设为192.168.1.1 第三步配置交换机1 system-view sysname LSW1 vlan batch 10 interface ethernet0/0/1 port link-type access port default vlan 10 qu…

解锁 Ryu API:从 Python 接口到 REST 设计全解析

Ryu 4.34 版本的 API 功能分类、核心接口说明及示例代码&#xff0c;结合其 Python 应用开发接口和 REST API 的设计特点进行综合解析&#xff1a; 一、Python 应用开发 API Ryu 的核心能力通过 Python 类库实现&#xff0c;开发者需继承 RyuApp 类并注册事件处理函数。 1. 应…

如何在需求分析阶段考虑未来扩展性

在需求分析阶段考虑未来扩展性的关键在于 前瞻规划、灵活架构、标准设计。其中&#xff0c;前瞻规划尤为重要&#xff0c;因为通过全面分析业务发展趋势与技术演进&#xff0c;能够在初期设计阶段预留足够扩展空间&#xff0c;降低后期改造成本&#xff0c;为企业长期发展奠定坚…

Docker搭建Redis哨兵模式【一主两从三哨兵】

Docker搭建Redis哨兵模式 系统: CentOS 7 Dockder 版本: VMware虚拟机 网络适配器 网络连接 桥接模式:直接连接物理网络查看IP命令 ip addr一、哨兵模式概述 1. 官方文档与关联博客 官方文档:https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel关联博…

关于统计建模大赛的选题

文章目录 0.大赛主题1.量化分析和风险管理2.金融市场预测与统计建模3.投资与机器学习相关4.大数据和医疗5.智能制造相关的6.教育行业 0.大赛主题 统计创新应用数据引领未来&#xff1a;这个主题其实很宽泛&#xff0c;没有什么明确的这个要求&#xff0c;所以只要是和我们的统…

Docker 学习笔记:从入门到部署,实战演练全流程!

&#x1f4cc; 开篇&#xff1a;为什么要学 Docker&#xff1f; 还在为环境不一致、部署麻烦、依赖冲突头疼吗&#xff1f;Docker 让一切变得简单&#xff01;作为现代开发和运维的神器&#xff0c;Docker 让我们可以用 一句命令 解决 “在我电脑上能跑” 的问题。今天&#x…

ThinkPhp 5 安装阿里云内容安全(绿化)

composer require alibabacloud/green-20220302 首先要把php5(不支持php7)的执行文件设置到PATH环境变量 此外还要先执行composer update php5.5和php5.6的区别 5.5认为 <? 开头的也是php文件&#xff0c;包括 <?php 5.6认为 <? 开头的不是php文件&#xff0c;只…

使用NVM工具管理Node版本

Date: 2025.03.10 14:53:55 author: lijianzhan NVM&#xff08;Node Version Manager&#xff09;用于在同一个系统上管理多个 Node.js 版本,NVM 允许你安装、使用和切换不同的 Node.js 版本。这对于前端工作人员来说可以更方便的管理和维护不同nodejs版本的项目。 &#xff0…

Vue主流的状态保存框架对比

一、Vuex 4&#xff08;官方传统方案&#xff09; 优点&#xff1a; 官方背书&#xff1a;Vue 官方长期维护&#xff0c;成熟稳定。结构化清晰&#xff1a;通过 state/mutations/actions/getters 强制约定代码结构&#xff0c;适合大型团队协作。插件生态&#xff1a;支持中间…

AIGC视频生成模型:慕尼黑大学、NVIDIA等的Video LDMs模型

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本文详细介绍慕尼黑大学携手 NVIDIA 等共同推出视频生成模型 Video LDMs。NVIDIA 在 AI 领域的卓越成就家喻户晓&#xff0c;而慕尼黑大学同样不容小觑&#xff0c;…

NVIDIA k8s-device-plugin源码分析与安装部署

在《kubernetes Device Plugin原理与源码分析》一文中&#xff0c;我们从源码层面了解了kubelet侧关于device plugin逻辑的实现逻辑&#xff0c;本文以nvidia管理GPU的开源github项目k8s-device-plugin为例&#xff0c;来看看设备插件侧的实现示例。 一、Kubernetes Device Pl…

C++ 数据结构详解及学习规划

C++数据结构详解及学习规划 一、C++常用数据结构详解与示例 以下是C++中核心数据结构的分类及具体实现示例: 1. 线性数据结构 a. 数组(Array) • 定义:存储固定大小、同类型元素的连续内存结构。 • 特点:快速随机访问(O(1)),但插入/删除效率低(O(n))。 • 应用场…

如何使用Postman,通过Mock的方式测试我们的API

这篇文章将教会大家如何利用 postman&#xff0c;通过 Mock 的方式测试我们的 API。 什么是 Mock Mock 是一项特殊的测试技巧&#xff0c;可以在没有依赖项的情况下进行单元测试。通常情况下&#xff0c;Mock 与其他方法的主要区别就是&#xff0c;用于取代代码依赖项的模拟对…

如何检查电脑的硬盘健康状况?

检查硬盘健康状况可以使用多种工具和方法。以下是一些常用的工具和步骤&#xff1a; Windows系统&#xff1a; 使用Windows内置工具&#xff1a; 磁盘检查&#xff1a;可以通过命令提示符&#xff08;cmd&#xff09;使用chkdsk命令来检查硬盘错误。例如&#xff0c;输入chkd…

JavaWeb中提供的对cookie的操作

JavaWeb中提供的对cookie的操作 简介服务端创建Cookie对象&#xff0c;然后将Cookie添加到HTTP响应结果中读取请求端浏览器的Cookie设置/读取Cookie在客户端的有效期URL编码/解码 简介 Servlet API为Servlet访问Cookie提供了简单易用的接口。javax.servlet.http.Cookie类用来表…

Android中AIDL和HIDL的区别

在Android中&#xff0c;AIDL&#xff08;Android Interface Definition Language&#xff09; 和 HIDL&#xff08;HAL Interface Definition Language&#xff09; 是两种用于定义跨进程通信接口的语言。AIDL 是 Android 系统最早支持的 IPC&#xff08;进程间通信&#xff0…