跳跃表原理及实现
一、跳表数据结构
跳表是有序表的一种,其底层是通过链表实现的。链表的特点是插入删除效率高,但是查找节点效率很低,最坏的时间复杂度是O(N),那么跳表就是解决这一痛点而生的。
为了提高查询效率,我们可以给链表加上索引,利用二分查找的思路,每两个节点抽取一个索引,根据数据规模,提升索引的高度,索引的最高层级为logN,那么跳跃表支持平均0 (1ogN),这样可以快读提高节点访问速度。由于在原始链表的基础上加索引,这些索引需要额外的存储空间,所以这是典型的通过空间换时间。下图简单描述跳跃表原理:

如果要访问8这个歌节点元素,在没有索引的情况下,需要遍历链表8次才能找到目标节点,但是通过跳表访问(1 -> 5 -> 7-> 7->7 -> 8) ,只需要访问6次,数据规模越大优势越明显。
对于提取索引,理论上每隔两个元素生成一个索引节点,但是在具体情况下,链表是动态的,删除和增加节点的时机很难确定,通过两个节点维护索引的方式开销成本很大。那么如何添加索引,一个新增节点要不要加索引,索引加到第几层,为了解决这个问题,可以通过投掷硬币的方式(随机数模2),连续投掷正面几次,那么这个次数就是索引的层级。
二、跳表代码实现
1、跳表结构、操作函数声明
#ifndef SKIPLINKLIST_H__
#define SKIPLINKLIST_H__#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <math.h>
#include <unistd.h>#define MAX_LEVEL 8//定义数据域
typedef int SkipLinkListData;typedef struct skiplinklistnode
{int level;SkipLinkListData data;struct skiplinklistnode* next;struct skiplinklistnode* down;} SkipLinkListNode;/*** 创建链表节点
*/
SkipLinkListNode* create_skiplinklist_node(SkipLinkListData data,int level);/*** 插入节点
*/
void insert_skiplinklist_node(SkipLinkListNode* head,SkipLinkListData data);/*** 维护索引
*/
void create_skiplinklist_index(SkipLinkListNode** index,SkipLinkListNode* node);/*** 随机数投硬币获取索引层高
*/
int random_skiplinklistnode_level();/*** 遍历跳表
*/
void show_skiplinglistnode_all(SkipLinkListNode* head);/*** 查询节点
*/
SkipLinkListNode* search_skiplinklistnode(SkipLinkListNode* head,SkipLinkListData data);/*** 删除跳表元素 重组索引 s* 删除的过程其实也是查找
*/
void delete_skiplinklistnode(SkipLinkListNode* head,SkipLinkListData data);#endif
2、跳表增删查操作定义
#include "./skiplinklist.h"SkipLinkListNode* create_skiplinklist_node(SkipLinkListData data,int level){SkipLinkListNode* node = (SkipLinkListNode*) malloc(sizeof(SkipLinkListNode));if(node==NULL){perror("create node fail");return NULL;}node->level = level;node->data = data;node->next = NULL;node->down = NULL;return node;
}void insert_skiplinklist_node(SkipLinkListNode *head, SkipLinkListData data)
{SkipLinkListNode *down_ptr = head->down;if (down_ptr == NULL){head->down = create_skiplinklist_node(data, 0);return;}int level = random_skiplinklistnode_level(); if(down_ptr->level<level){level = down_ptr->level +1;}SkipLinkListNode* index_node = NULL;/// 当前层级小于随机高度时候,需要升级索引if(down_ptr->level<level){/// 向上升级一层索引level = down_ptr->level +1;SkipLinkListNode* down_node = create_skiplinklist_node(down_ptr->data,level);index_node = create_skiplinklist_node(data,level);down_node->next = index_node;down_node->down = down_ptr;head->down = down_node;} /// 搜索节点while (down_ptr!= NULL && down_ptr->data<=data && down_ptr->level>0){SkipLinkListNode* next_ptr = down_ptr->next;// 查找当前层级索引,定位到离当前数值的最大索引值while (next_ptr != NULL && next_ptr->data<=data && next_ptr->next!=NULL){next_ptr = next_ptr->next;}/// 维护索引if(down_ptr->level<=level){SkipLinkListNode* new_node = create_skiplinklist_node(data, down_ptr->level);if(next_ptr==NULL){/// 如果当前层索引到达最后一个值,则跳到下一层索引down_ptr->next=new_node;}else{new_node->next = next_ptr->next;next_ptr->next = new_node; }create_skiplinklist_index(&index_node,new_node);}///跳转下一层索引down_ptr = next_ptr != NULL?next_ptr->down:down_ptr->down; }/// 遍历链表 数据插入链表while (down_ptr != NULL&&down_ptr->next!=NULL&&down_ptr->next->data<data){down_ptr = down_ptr->next;}SkipLinkListNode* node = create_skiplinklist_node(data, 0);SkipLinkListNode* next_node = down_ptr->next;down_ptr->next = node;node->next = next_node;if(index_node!=NULL){create_skiplinklist_index(&index_node,node);}
}void create_skiplinklist_index(SkipLinkListNode** index_node,SkipLinkListNode* new_node)
{if ((*index_node) == NULL){(*index_node) = new_node;}else{SkipLinkListNode* tmp_node = (*index_node);while (tmp_node != NULL){if (tmp_node->down == NULL){tmp_node->down = new_node;break;}tmp_node = tmp_node->down;}}
}int random_skiplinklistnode_level()
{int level = 0;int mod = 2;while (rand() % mod == 0 ){level++;}return level>=MAX_LEVEL?MAX_LEVEL:level;
}void show_skiplinglistnode_all(SkipLinkListNode* head)
{SkipLinkListNode* down_ptr = head->down;while (down_ptr!=NULL){if(down_ptr->level==0){printf("原 始链表: %d ",down_ptr->data);}else{printf("第%d层索引: %d ",down_ptr->level,down_ptr->data);}SkipLinkListNode* next_ptr = down_ptr->next;while (next_ptr!=NULL){printf("%d ",next_ptr->data);next_ptr = next_ptr->next;}down_ptr= down_ptr->down; printf("\n");}printf("\n");
}SkipLinkListNode* search_skiplinklistnode(SkipLinkListNode* head,SkipLinkListData data)
{SkipLinkListNode* down_ptr = head->down;/// 索引查找while (down_ptr!=NULL && down_ptr->data<=data && down_ptr->level>0){printf("遍历第%d层次节点:%d\n",down_ptr->level,down_ptr->data);if(down_ptr->next!=NULL && down_ptr->next->data>data){down_ptr = down_ptr->down;continue;}SkipLinkListNode* next_ptr = down_ptr->next;while (next_ptr != NULL && next_ptr->data<=data && next_ptr->next!=NULL&& next_ptr->next->data<=data){next_ptr = next_ptr->next;printf("遍历第%d层次节点:%d\n",next_ptr->level,next_ptr->data);}///跳转下一层索引down_ptr = next_ptr != NULL?next_ptr->down:down_ptr->down; }//到达底层链表 遍历目标值while (down_ptr!=NULL){if(down_ptr->data==data){printf("遍历第%d层次节点,命中目标%d\n",down_ptr->level,down_ptr->data);return down_ptr;}down_ptr = down_ptr->next;}printf("遍历结束目标节点%d不存在\n",data);printf("\n");return NULL;
}void delete_skiplinklistnode(SkipLinkListNode *head, SkipLinkListData data)
{printf("删除元素开始\n");SkipLinkListNode *down_ptr = head->down;while (down_ptr != NULL && down_ptr->data < data && down_ptr->level > 0){printf("遍历第%d层次节点:%d\n", down_ptr->level, down_ptr->data);if (down_ptr->next != NULL && down_ptr->next->data>=data){/// 处理要删除的节点存在索引节点if(down_ptr->next->data==data){SkipLinkListNode* index_ptr = down_ptr->next;down_ptr->next = down_ptr->next->next;printf("删除第%d层索引%d\n",index_ptr->level,index_ptr->data);free(index_ptr);}down_ptr = down_ptr->down;continue;}SkipLinkListNode *next_ptr = down_ptr->next;while (next_ptr != NULL && next_ptr->data < data && next_ptr->next != NULL && next_ptr->next->data <= data){if(next_ptr->next->data==data){SkipLinkListNode* index_node= next_ptr->next;next_ptr->next = next_ptr->next->next;free(index_node);continue;}next_ptr = next_ptr->next;printf("遍历第%d层次节点:%d\n", next_ptr->level, next_ptr->data);}/// 跳转下一层索引down_ptr = next_ptr != NULL ? next_ptr->down : down_ptr->down;}while (down_ptr!=NULL){if(down_ptr->next!=NULL && down_ptr->next->data==data){SkipLinkListNode* traget_node = down_ptr->next;down_ptr->next = down_ptr->next->next;free(traget_node);}down_ptr=down_ptr->next;}printf("删除元素结束\n");}
三、跳表测试
void test_skiplinklist()
{SkipLinkListNode* head = create_skiplinklist_node(0,0);SkipLinkListData i;int c = 30;for(i=1;i<c;i++){insert_skiplinklist_node(head,i); }show_skiplinglistnode_all(head);search_skiplinklistnode(head,28);delete_skiplinklistnode(head,15);show_skiplinglistnode_all(head);}

相关文章:
跳跃表原理及实现
一、跳表数据结构 跳表是有序表的一种,其底层是通过链表实现的。链表的特点是插入删除效率高,但是查找节点效率很低,最坏的时间复杂度是O(N),那么跳表就是解决这一痛点而生的。 为了提高查询效率,我们可以给链表加上索…...
详解Vue3中的鼠标事件mousemove、mouseover和mouseout
本文主要介绍Vue3中的常见鼠标事件mousemove、mouseover和mouseout。 目录 一、mousemove——鼠标移动事件二、mouseover——鼠标移入事件三、mouseout——鼠标移出事件 下面是Vue 3中常用的鼠标事件mousemove、mouseover和mouseout的详解。 一、mousemove——鼠标移动事件 鼠…...
Java:socket编程
目录 1、主程序 2、socket任务类 3、jdbc任务类 4、tomcat-jdbc连接池 5、jar包依赖 1、主程序 创建2个线程池,一个用于管理socket连接,一个用来管理jdbc连接。 package socket;import java.io.IOException; import java.net.ServerSocket; import…...
哨兵1号回波数据(L0级)FDBAQ压缩算法详解
本专栏目录: 全球SAR卫星大盘点与回波数据处理专栏目录-CSDN博客 1. 全球SAR卫星回波数据压缩算法统计 各国的SAR卫星的压缩算法按照时间轴排列如下: 可以看出传统的分块BAQ压缩算法(上图粉色)仍然是主流,哨兵1号其实也有传统的BAQ压缩模式。 本文介绍哨兵1号用的FDBAQ算…...
盾构机数据可视化监控平台 | 图扑数字孪生
2002 年,中国 863 计划把盾构机列为国家关键技术,以国家力量为主导,集中力量进行盾构机专项研究。在 2008 年,中国成功研制出属于自己的国产盾构机——中国中铁一号,同时还打通了天津地铁 1500m 的隧道。此举更彻底地打破了国内盾…...
计算机网络课程设计-企业网三层架构
(单人版) 摘 要 本篇报告主要解决了为一家名为西宫的公司网络搭建问题,该网络采用企业网三层架构对完了过进行设计。首先使用以太网中继,主要使用VLAN划分的技术来划定不同部门。使用MSTP对每个组配置生成树,防止交换机…...
Docker上传镜像到Harbor
上传镜像到Harbor 给镜像打上标签 语法 docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/] docker tag eureka:v1 127.0.0.1:85/tensquare/eureka:v1推送镜像 docker push 127.0.0.12:85/tensquare/eureka:v1如果出现以下错误,是因为Docker没有把Har…...
mfc100u.dll文件丢失了要怎么解决?修复mfc100u.dll详细指南
mfc100u.dll文件丢失了要怎么解决?首先让我们扒一扒什么是 mfc100u.dll。这玩意儿是 Microsoft Visual Studio 2010 的一部分,它就像一款程序生活中不可或缺的零件,没了它,程序肯定跑不起来。想想看,没有一个重要的零件ÿ…...
【ArcGIS微课1000例】0084:甘肃积石山地震震中100km范围内历史灾害点分布图(2005-2020)
甘肃积石山地震震中100km范围内历史灾害点分布图(2005-2020)。 文章目录 一、成果预览二、实验数据三、符号化四、地图整饰一、成果预览 本实验最终效果图如下所示: 二、实验数据 以下数据可以从本专栏配套的实验数据包中0084.rar中获取。 1. 历史灾害数据。为2005-2020时…...
java SSM拖拉机售后管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计
一、源码特点 java SSM拖拉机售后管理系统是一套完善的web设计系统(系统采用SSM框架进行设计开发,springspringMVCmybatis),对理解JSP java编程开发语言有帮助,系统具有完整的源 代码和数据库,系统主要…...
侯捷C++ 2.0 新特性
关键字 nullptr and std::nullptr_t auto 一致性初始化:Uniform Initialization 11之前,初始化方法包括:小括号、大括号、赋值号,这让人困惑。基于这个原因,给他来个统一,即,任何初始化都能够…...
计算机网络——基础知识汇总(八)
个人名片: 🦁作者简介:一名喜欢分享和记录学习的在校大学生 🐯个人主页:妄北y 🐧个人QQ:2061314755 🐻个人邮箱:2061314755qq.com 🦉个人WeChat:V…...
DIA数皆智能客户体验管理CEM获伊利“健康+AI”生态创新大奖
DIA数皆智能客户体验管理CEM获伊利“健康AI”生态创新大奖 数皆智能再获殊荣! 北京,2023年12月26日 — 在全球瞩目的伊利集团“健康AI”生态创新大赛中,上海数皆智能技术有限公司大放异彩,其创新领先的“智能化客户体验管理CEM&a…...
linux 休眠唤醒中设备、总线、用户进程、内核线程调试分析流程
一、suspending consoles打印 代码位置:Kernel/power/suspend.c 函数调用流程:devices_and_enter(suspend_state_t state) --> suspend_console(); void suspend_console(void) { if (!console_suspend_enabled) 注释这一行,可以看到…...
k8s陈述式资源管理(命令行)
1、资源管理 (1)陈述式资源管理(常用——查、增) 使用kubectl工具进行命令行管理 ①特点:对资源的增删查比较方便,对改不友好 ②优点:90%以上的场景都可以满足 ③缺点:命令冗长…...
五、HTML 标题
在 HTML 文档中,标题很重要。 一、HTML 标题 标题(Heading)是通过 <h1> - <h6> 标签进行定义的。<h1> 定义最大的标题。 <h6> 定义最小的标题。 <h1>这是一个标题。</h1> <h2>这是一个标题。&l…...
三菱MR-JE伺服脉冲轴应用参数设置
三菱MR-JE伺服在脉冲轴控制上的应用,常用参数设置如下: 1、常用参数 未完......
通信原理课设(gec6818) 006:网络编程
目录 1、概念 2、通信 3、通信基本流程 TCP: UDP: 4、函数 I 创建套接字 II 绑定地址 III 字节序转换 IV 地址转换 V 监听 VI accept VII connect VIII 从套接字接收信息 IX 从套接字发送消息 X 关闭套接字 5、网络配置 1、确保你的网卡里面有两个虚拟网卡&a…...
一体化、一站式!智能视频客服加码全媒体云呼叫中心能力
凭借对电话、短信、邮件、社交媒体、视频等数种沟通渠道强大的统一集成能力,全媒体云呼叫中心已跃升成为现代企业客户服务的核心工具,高效便捷地为企业提供客户服务。而随着消费者需求愈加多元化和个性化,传统的语音通话方式已无法满足部分消…...
Vue的watch功能:实现响应式数据更新
watch是vue内部提供的一个用于侦听功能的更通用的方法,其用来响应数据的变化,通过特定的数据变化驱动一些操作。简言之:当需要被watch监听的数据发生变化时就会被执行watch中的逻辑。实现数据的实时更新! 普通监听 <template…...
手把手教你用ESP8266 AT指令连接华为云IoT(附固件烧录与MQTT避坑指南)
从零玩转ESP8266:华为云IoT连接实战与深度排错指南 当你第一次拿到那块拇指大小的ESP8266模块时,可能不会想到这个售价不到20元的Wi-Fi芯片能成为物联网世界的通行证。作为全球使用量最大的IoT连接方案之一,ESP8266配合华为云物联网平台&…...
Dark Reader实用指南:解决夜间浏览痛点的高效方案
Dark Reader实用指南:解决夜间浏览痛点的高效方案 【免费下载链接】darkreader Dark Reader Chrome and Firefox extension 项目地址: https://gitcode.com/gh_mirrors/da/darkreader 在数字时代,我们每天面对屏幕的时间越来越长,尤其…...
别死记硬背了!用Python的NumPy库,5分钟搞定线性代数里的矩阵运算(附代码)
用Python的NumPy库轻松玩转线性代数:矩阵运算实战指南 线性代数作为现代科学与工程的基石,在机器学习、计算机图形学、量化金融等领域无处不在。但传统教材中抽象的数学符号和繁琐的手工计算,往往让学习者望而生畏。今天,我们将用…...
哲学家吃饭问题没搞懂?用Python模拟信号量帮你彻底理解进程同步(附可运行代码)
用Python动态模拟哲学家进餐问题:从死锁到解决方案的完整实践指南 在操作系统的学习中,哲学家进餐问题堪称进程同步与死锁的"经典案例"。这个看似简单的场景却蕴含着并发编程中最棘手的挑战——如何协调多个进程对有限资源的访问。本文将带你…...
解锁自定义键盘体验:用Vial-QMK打造个性化配置指南
解锁自定义键盘体验:用Vial-QMK打造个性化配置指南 【免费下载链接】vial-qmk QMK fork with Vial-specific features. 项目地址: https://gitcode.com/gh_mirrors/vi/vial-qmk 核心价值:为什么选择Vial-QMK定制键盘? 在机械键盘的世…...
告别手动修改!用Env文件管理器一键配置Allegro SKILL加载路径(支持16.6/17.4)
告别手动修改!用Env文件管理器一键配置Allegro SKILL加载路径(支持16.6/17.4) 在PCB设计领域,Allegro作为行业标杆工具,其强大的可扩展性离不开SKILL脚本的支持。然而,随着项目复杂度提升,SKILL…...
基于MATLAB的平移线扫激光三维重建完整方案与代码实现
现整理了一套完整的,平移线扫重建 matlab代码和方案,包含相机标定、光平面标定与方案、移动装置标定与方案、激光线条中心线自适应提取、畸变矫正、三维重建、点云滤波等部分,代码按模块编写,注释完整,附带一份完整苹果…...
如何突破Windows权限壁垒?系统管理专家的秘密武器
如何突破Windows权限壁垒?系统管理专家的秘密武器 【免费下载链接】NSudo [Deprecated, work in progress alternative: https://github.com/M2Team/NanaRun] Series of System Administration Tools 项目地址: https://gitcode.com/gh_mirrors/ns/NSudo 在W…...
UE5场景过曝/白屏排查指南:从后期处理体积到项目设置的实战修复
1. 当UE5场景变成"雪盲症"时该怎么办? 第一次打开UE5项目看到白茫茫一片的时候,我差点以为显卡烧了。这种场景过曝现象就像在雪山没戴墨镜,所有细节都被强光吞噬。新手遇到这种情况别慌,我整理了从"急救措施"…...
深入解析:高级 Android 开发工程师职位与面试全攻略
引言:移动互联网时代的核心力量 在当今移动互联网蓬勃发展的时代,智能手机已成为人们日常生活中不可或缺的一部分。作为连接用户与数字服务的桥梁,移动应用扮演着至关重要的角色。而在移动应用的生态中,Android 系统凭借其开放性和庞大的用户基础,占据了全球移动操作系统…...
