当前位置: 首页 > news >正文

跳跃表原理及实现

一、跳表数据结构

         跳表是有序表的一种,其底层是通过链表实现的。链表的特点是插入删除效率高,但是查找节点效率很低,最坏的时间复杂度是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);}

相关文章:

跳跃表原理及实现

一、跳表数据结构 跳表是有序表的一种&#xff0c;其底层是通过链表实现的。链表的特点是插入删除效率高&#xff0c;但是查找节点效率很低&#xff0c;最坏的时间复杂度是O(N)&#xff0c;那么跳表就是解决这一痛点而生的。 为了提高查询效率&#xff0c;我们可以给链表加上索…...

详解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个线程池&#xff0c;一个用于管理socket连接&#xff0c;一个用来管理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 计划把盾构机列为国家关键技术&#xff0c;以国家力量为主导&#xff0c;集中力量进行盾构机专项研究。在 2008 年&#xff0c;中国成功研制出属于自己的国产盾构机——中国中铁一号&#xff0c;同时还打通了天津地铁 1500m 的隧道。此举更彻底地打破了国内盾…...

计算机网络课程设计-企业网三层架构

&#xff08;单人版&#xff09; 摘 要 本篇报告主要解决了为一家名为西宫的公司网络搭建问题&#xff0c;该网络采用企业网三层架构对完了过进行设计。首先使用以太网中继&#xff0c;主要使用VLAN划分的技术来划定不同部门。使用MSTP对每个组配置生成树&#xff0c;防止交换机…...

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如果出现以下错误&#xff0c;是因为Docker没有把Har…...

mfc100u.dll文件丢失了要怎么解决?修复mfc100u.dll详细指南

mfc100u.dll文件丢失了要怎么解决?首先让我们扒一扒什么是 mfc100u.dll。这玩意儿是 Microsoft Visual Studio 2010 的一部分&#xff0c;它就像一款程序生活中不可或缺的零件&#xff0c;没了它&#xff0c;程序肯定跑不起来。想想看&#xff0c;没有一个重要的零件&#xff…...

【ArcGIS微课1000例】0084:甘肃积石山地震震中100km范围内历史灾害点分布图(2005-2020)

甘肃积石山地震震中100km范围内历史灾害点分布图(2005-2020)。 文章目录 一、成果预览二、实验数据三、符号化四、地图整饰一、成果预览 本实验最终效果图如下所示: 二、实验数据 以下数据可以从本专栏配套的实验数据包中0084.rar中获取。 1. 历史灾害数据。为2005-2020时…...

java SSM拖拉机售后管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM拖拉机售后管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源 代码和数据库&#xff0c;系统主要…...

侯捷C++ 2.0 新特性

关键字 nullptr and std::nullptr_t auto 一致性初始化&#xff1a;Uniform Initialization 11之前&#xff0c;初始化方法包括&#xff1a;小括号、大括号、赋值号&#xff0c;这让人困惑。基于这个原因&#xff0c;给他来个统一&#xff0c;即&#xff0c;任何初始化都能够…...

计算机网络——基础知识汇总(八)

个人名片&#xff1a; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生 &#x1f42f;个人主页&#xff1a;妄北y &#x1f427;个人QQ&#xff1a;2061314755 &#x1f43b;个人邮箱&#xff1a;2061314755qq.com &#x1f989;个人WeChat&#xff1a;V…...

DIA数皆智能客户体验管理CEM获伊利“健康+AI”生态创新大奖

DIA数皆智能客户体验管理CEM获伊利“健康AI”生态创新大奖 数皆智能再获殊荣&#xff01; 北京&#xff0c;2023年12月26日 — 在全球瞩目的伊利集团“健康AI”生态创新大赛中&#xff0c;上海数皆智能技术有限公司大放异彩&#xff0c;其创新领先的“智能化客户体验管理CEM&a…...

linux 休眠唤醒中设备、总线、用户进程、内核线程调试分析流程

一、suspending consoles打印 代码位置&#xff1a;Kernel/power/suspend.c 函数调用流程&#xff1a;devices_and_enter(suspend_state_t state) --> suspend_console(); void suspend_console(void) { if (!console_suspend_enabled) 注释这一行&#xff0c;可以看到…...

k8s陈述式资源管理(命令行)

1、资源管理 &#xff08;1&#xff09;陈述式资源管理&#xff08;常用——查、增&#xff09; 使用kubectl工具进行命令行管理 ①特点&#xff1a;对资源的增删查比较方便&#xff0c;对改不友好 ②优点&#xff1a;90%以上的场景都可以满足 ③缺点&#xff1a;命令冗长…...

五、HTML 标题

在 HTML 文档中&#xff0c;标题很重要。 一、HTML 标题 标题&#xff08;Heading&#xff09;是通过 <h1> - <h6> 标签进行定义的。<h1> 定义最大的标题。 <h6> 定义最小的标题。 <h1>这是一个标题。</h1> <h2>这是一个标题。&l…...

三菱MR-JE伺服脉冲轴应用参数设置

三菱MR-JE伺服在脉冲轴控制上的应用&#xff0c;常用参数设置如下&#xff1a; 1、常用参数 未完......

通信原理课设(gec6818) 006:网络编程

目录 1、概念 2、通信 3、通信基本流程 TCP: UDP: 4、函数 I 创建套接字 II 绑定地址 III 字节序转换 IV 地址转换 V 监听 VI accept VII connect VIII 从套接字接收信息 IX 从套接字发送消息 X 关闭套接字 5、网络配置 1、确保你的网卡里面有两个虚拟网卡&a…...

一体化、一站式!智能视频客服加码全媒体云呼叫中心能力

凭借对电话、短信、邮件、社交媒体、视频等数种沟通渠道强大的统一集成能力&#xff0c;全媒体云呼叫中心已跃升成为现代企业客户服务的核心工具&#xff0c;高效便捷地为企业提供客户服务。而随着消费者需求愈加多元化和个性化&#xff0c;传统的语音通话方式已无法满足部分消…...

Vue的watch功能:实现响应式数据更新

watch是vue内部提供的一个用于侦听功能的更通用的方法&#xff0c;其用来响应数据的变化&#xff0c;通过特定的数据变化驱动一些操作。简言之&#xff1a;当需要被watch监听的数据发生变化时就会被执行watch中的逻辑。实现数据的实时更新&#xff01; 普通监听 <template…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测

uniapp 中配置 配置manifest 文档&#xff1a;manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号&#xff1a;4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...

GO协程(Goroutine)问题总结

在使用Go语言来编写代码时&#xff0c;遇到的一些问题总结一下 [参考文档]&#xff1a;https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现&#xff1a; 今天在看到这个教程的时候&#xff0c;在自己的电…...