数据结构与算法—跳表(skiplist)
目录
前言
跳表
查询时间分析
1、时间复杂度 o(logn)
2、空间复杂度O(n)
动态插入和删除
跳表动态更新
跳表与红黑树比较
跳表实现
前言
二分查找用的数组
链表可不可以实现二分查找呢?
跳表
各方面性能比较优秀的动态数据结构,可以支持快速插入、删除、查找操作。写起来也不复杂,甚至可以代替红黑树。
跳表特点
对链表建立一级索引,每两个节点提取一个节点到上一级,叫做索引节点。
加一层索引之后,查找一个结点需要遍历的节点个数减少了,也就是查找效率提高了。
这种查找方式就是跳表
查询时间分析
1、时间复杂度 o(logn)
- 在单链表中查询某个数据的时间复杂度O(N)
- 跳表,n个节点,会有几层索引。
- 第k级索引的节点个数是k-1级索引节点个数的1/2;即 第k级索引节点的个数 n/2^k
- k = log2n-1,如果包含原始链表层,整个跳表的高度是log2 n
- 在跳表查找某个数据时,如果每一层需要遍历m个节点,那么跳表查询一个数据的时间复杂度O(m*logn)
- m是多少? 每一级索引最多只需要遍历3个节点, m = 3 常数
2、空间复杂度O(n)
索引节点的个数 n/2+n/4+n/8…+8+4+2=n-2 ,所以跳表的空间复杂度O(n)
意思是n个节点的单链表改成跳表,需要额外再用接近n个节点的内存空间。
在软件开发中原始链表中存储的有可能是很大的对象,而索引节点只需要存储关键值和几个指针,并不需要存储对象。所以当对象所以节点很大时,索引占用的空间可以忽略不计了。
动态插入和删除
插入、删除操作时间复杂度O(logn)
查找,需要遍历每个节点。查找时间复杂度O(logn)
删除,要拿到前驱节点,如果是双向链表,不需要考虑这个问题。
跳表动态更新
- 插入数据,如果跳表不更新,跳表会退化成单链表。
- 跳表通过随机函数维护 “平衡性”
- 往跳表中插入数据时,同时将数据插入到索引层中。(哪个索引层?)
通过随机函数,决定这个点插入到哪几级索引,如随机函数生成K,则节点添加到第一级到第K级索引中。
跳表与红黑树比较
1,按照区间查找数据,红黑树的效率没有跳表高。跳表O(logn)
2、跳表灵活,通过改变索引结构,有效平衡执行效率和内存消耗
3、红黑树一般有现成的可用,跳表需要自己实现
跳表实现
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>typedef struct _node
{int key;int value;int max_level;struct _node *next[0];
}node;typedef struct _skiplist
{int level;int count;node *head;
}skiplist;#define offsetof(TYPE,MEMBER) ((size_t) & ((TYPE *)0)->MEMBER)
#define container(ptr,type,member) ({\const typeof( ((type *)0)->member) *__mptr = (ptr);\(type *) ( (char *)__mptr - offsetof(type,member));})node * skip_list_create_node(int level,int key,int value)
{node *tmp = NULL;tmp = (node *)malloc(sizeof(node) + level*sizeof(node*));assert(tmp !=NULL);memset(tmp,0,sizeof(node) + level * sizeof(node*));tmp->key = key;tmp->value = value;tmp->max_level = level;return tmp;
}
skiplist *skip_list_create(int max_level)
{int i=0;skiplist *list = NULL;list = (skiplist *)malloc(sizeof(skiplist));assert(list != NULL);list->level = 1;list->count = 0;list->head = skip_list_create_node(max_level,0,0);if(list->head == NULL){free(list);return NULL;}return list;
}
void skip_list_destory(skiplist *list)
{int i = 0;node *tmp = NULL;if((list == NULL) || list->head == NULL){return ;}while(list->head->next[0] != NULL){tmp = list->head->next[0];list->head->next[0] = tmp->next[0];free(tmp);}free(list->head);free(list);return;
}int skip_list_level(skiplist *list)
{int i = 0;int level = 1;for(i = 0;i<list->head->max_level;i++){if(rand()%2 == 1)level++;}return level;
}int skip_list_insert(skiplist *list,int key,int value)
{int i = 0;int level = 0;node **update = NULL;node *tmp = NULL;node *prev = NULL;if(list == NULL)return 1;update = (node **)malloc(sizeof(node *)*list->head->max_level);if(update == NULL)return 2;prev = list->head;for(i = (list->level - 1);i>=0;i--){while(((tmp = prev->next[i]) != NULL) && (tmp->key < key)){prev = tmp;}update[i] = prev;}if((tmp != NULL) && (tmp->key == key))return 3;//get ramdon levellevel = skip_list_level(list);tmp = skip_list_create_node(level,key,value);if(tmp == NULL)return 4;//update max level,updateif(level > list->level){for(i = list->level;i<level;i++){update[i] = list->head;}list->level = level;}//update node pointerfor(i = 0;i<level;i++){tmp->next[i] = update[i]->next[i];update[i]->next[i] = tmp;}list->count++;return 0;
}int skip_list_search(skiplist *list,int key,int *value)
{int i=0;node *prev = NULL;node *tmp = NULL;if((list == NULL) || (list->count == 0) || (value == NULL)){return 1;}prev = list->head;for(i = list->level - 1;i >= 0;i--){//while(((tmp == prev->next[i])!=NULL) && (tmp->key<=key))while(((tmp = prev->next[i]) != NULL) && (tmp->key <= key)){if(tmp->key == key){*value = tmp->value;return 0;}prev = tmp;}}return -1;
}void skip_list_dump(skiplist *list)
{int i = 0;node *ptmp = NULL;printf("\r\n skiplist level[%d],count[%d]",list->level,list->count);for(i = list->level -1 ;i>=0;i--){ptmp = list->head->next[i];printf("\r\n level[%d]:",i);while(ptmp != NULL){printf("%d-%d ",ptmp->key,ptmp->value);ptmp = ptmp->next[i];}}printf("\r\n-----------------------------------\n");return;
}int skip_list_delete(skiplist *list,int key,int *value)
{int i = 0;node **update = NULL;node *tmp = NULL;node *prev = NULL;if((list == NULL) && value == NULL || list->count ==0)return 1;update = (node **)malloc(sizeof(node *)*list->level);if(update == NULL)return 2;prev = list->head;for(i = (list->level - 1);i>=0;i++){while((tmp = prev->next[i]) != NULL && (tmp->key < key)){prev=tmp;}update[i] = prev;}if((tmp != NULL) && (tmp->key == key)){*value = tmp->value;for(i = 0;i<list->level;i++){if(update[i]->next[i] == tmp){update[i]->next[i] = tmp->next[i];}}free(tmp);tmp = NULL;for(i = list->level -1 ;i>=0 ;i++){if(list->head->next[i]==NULL)list->level--;elsebreak;}list->count--;}elsereturn 3;//not findreturn 0;
}int main()
{int res = 0;int key = 0;int value = 0;skiplist *list = NULL;list = skip_list_create(8);assert(list != NULL);int i=0;for(i = 0;i<30;i++){key = value = i;res = skip_list_insert(list,key,value);if(res !=0){printf("insert error res=%d\n",res);}}printf("insert down\n");skip_list_dump(list);printf("############search#######\n");key = 10;skip_list_search(list,key,&value);printf("search value=%d\n",value);printf("############del############\n");skip_list_delete(list,key,&value);printf("del value=%d\n",value); skip_list_dump(list);
}
# ./a.out
insert downskiplist level[8],count[30]level[7]:15-15 level[6]:15-15 16-16 23-23 level[5]:0-0 1-1 5-5 8-8 14-14 15-15 16-16 19-19 22-22 23-23 24-24 level[4]:0-0 1-1 3-3 4-4 5-5 6-6 7-7 8-8 13-13 14-14 15-15 16-16 17-17 18-18 19-19 22-22 23-23 24-24 27-27 29-29 level[3]:0-0 1-1 2-2 3-3 4-4 5-5 6-6 7-7 8-8 9-9 10-10 13-13 14-14 15-15 16-16 17-17 18-18 19-19 20-20 21-21 22-22 23-23 24-24 25-25 26-26 27-27 28-28 29-29 level[2]:0-0 1-1 2-2 3-3 4-4 5-5 6-6 7-7 8-8 9-9 10-10 11-11 12-12 13-13 14-14 15-15 16-16 17-17 18-18 19-19 20-20 21-21 22-22 23-23 24-24 25-25 26-26 27-27 28-28 29-29 level[1]:0-0 1-1 2-2 3-3 4-4 5-5 6-6 7-7 8-8 9-9 10-10 11-11 12-12 13-13 14-14 15-15 16-16 17-17 18-18 19-19 20-20 21-21 22-22 23-23 24-24 25-25 26-26 27-27 28-28 29-29 level[0]:0-0 1-1 2-2 3-3 4-4 5-5 6-6 7-7 8-8 9-9 10-10 11-11 12-12 13-13 14-14 15-15 16-16 17-17 18-18 19-19 20-20 21-21 22-22 23-23 24-24 25-25 26-26 27-27 28-28 29-29
-----------------------------------
############search############
search value=10
############del############
del value=10skiplist level[8],count[29]level[7]:15-15 level[6]:15-15 16-16 23-23 level[5]:0-0 1-1 5-5 8-8 14-14 15-15 16-16 19-19 22-22 23-23 24-24 level[4]:0-0 1-1 3-3 4-4 5-5 6-6 7-7 8-8 13-13 14-14 15-15 16-16 17-17 18-18 19-19 22-22 23-23 24-24 27-27 29-29 level[3]:0-0 1-1 2-2 3-3 4-4 5-5 6-6 7-7 8-8 9-9 13-13 14-14 15-15 16-16 17-17 18-18 19-19 20-20 21-21 22-22 23-23 24-24 25-25 26-26 27-27 28-28 29-29 level[2]:0-0 1-1 2-2 3-3 4-4 5-5 6-6 7-7 8-8 9-9 11-11 12-12 13-13 14-14 15-15 16-16 17-17 18-18 19-19 20-20 21-21 22-22 23-23 24-24 25-25 26-26 27-27 28-28 29-29 level[1]:0-0 1-1 2-2 3-3 4-4 5-5 6-6 7-7 8-8 9-9 11-11 12-12 13-13 14-14 15-15 16-16 17-17 18-18 19-19 20-20 21-21 22-22 23-23 24-24 25-25 26-26 27-27 28-28 29-29 level[0]:0-0 1-1 2-2 3-3 4-4 5-5 6-6 7-7 8-8 9-9 11-11 12-12 13-13 14-14 15-15 16-16 17-17 18-18 19-19 20-20 21-21 22-22 23-23 24-24 25-25 26-26 27-27 28-28 29-29
-----------------------------------
相关文章:
数据结构与算法—跳表(skiplist)
目录 前言 跳表 查询时间分析 1、时间复杂度 o(logn) 2、空间复杂度O(n) 动态插入和删除 跳表动态更新 跳表与红黑树比较 跳表实现 前言 二分查找用的数组 链表可不可以实现二分查找呢? 跳表 各方面性能比较优秀的动态数据结构,可以支持快速…...
【C++】5.C/C++内存管理
1.C/C内存管理 int globalVar 1; static int staticGlobalVar 1; void Test() {static int staticVar 1;int localVar 1;int num1[10] {1, 2, 3, 4};char char2[] "abcd";char* pChar3 "abcd";int* ptr1 (int*)malloc(sizeof (int)*4);int* ptr2 …...
一文让你彻底理解关于消息队列的使用
一、消息队列概述 消息队列中间件是分布式系统中重要的组件,主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构。目前使用较多的消息队列有ActiveMQ,Rabbit…...
条件期望3
条件期望例题—连续发生的事情 连续地做二项实验, 每一次成功概率为p. 当连续k次成功时, 停止实验. 求停止实验时做的总实验次数的期望. 解: 错误解法 设NkN_kNk为停止实验时做的总实验次数, 则 E[Nk]E[E[Nk∣Nk−1]]∑jk−1∞E[Nk∣Nk−1j]\begin{split} E[N_k] & E[E…...
第四届蓝桥杯省赛 C++ B组 - 翻硬币
✍个人博客:https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 📚专栏地址:蓝桥杯题解集合 📝原题地址:翻硬币 📣专栏定位:为想参加蓝桥杯的小伙伴整理常考算法题解,祝大家都…...
linux shell 入门学习笔记14 shell脚本+数学计算
概念 把复杂的命令执行过程,通过逻辑代码,组成一个脚本文件的方式就叫做shell脚本。 shebang #! /bin/bash #! /bin/perl #! /bin/python执行脚本的方式 source my_first.sh . my_first.shbash my_first.sh ./my_first.sh变量引用 ${var} 取出变量结果 …...
ESP32设备驱动-MAX30100心率监测传感器驱动
MAX30100心率监测传感器驱动 1、MAX30100介绍 MAX30100 是一款集成脉搏血氧饱和度和心率监测传感器解决方案。 它结合了两个 LED、一个光电探测器、优化的光学器件和低噪声模拟信号处理,以检测脉搏血氧饱和度和心率信号。 MAX30100 采用 1.8V 和 3.3V 电源供电,可通过软件…...
RTD2169芯片停产|完美替代RTD2169芯片|CS5260低BOM成本替代RTD2169方案设计
RTD2169芯片停产|完美替代RTD2169芯片|CS5260低BOM成本替代RTD2169方案设计 瑞昱的RTD2169芯片目前已经停产了, 那么之前用RTD2169来设计TYPEC转VGA方案的产品,该如何生产这类产品?且RTD2169芯片价格较贵,芯片封装尺寸是QFN40&…...
urho3d数据库
只有在启用以下两个构建选项之一时,数据库子系统才会构建到Urho3D库中:Urho3D_Database_ODBC和Urho3D-Database_SQLITE。当两个选项都启用时,URHO3D_DATABASE_ODBC优先。这些构建选项决定子系统将使用哪个数据库API。ODBC DB API更适用于本地…...
141. 周期
Powered by:NEFU AB-IN Link 文章目录141. 周期题意思路代码141. 周期 题意 一个字符串的前缀是从第一个字符开始的连续若干个字符,例如 abaab 共有 5个前缀,分别是 a,ab,aba,abaa,abaab。 我们希望知道一…...
Windows下命令执行绕过技巧总结(渗透测试专用)
一、连接符1、双引号不要求双引号闭合举例:"who"a"mi" //闭合的 "who"a"mi //不闭合的2、圆括号必须在两边,不能包括中间的字符。举例:((whoami))3、^符号(转译符号)不可以在结尾&…...
mindspore的MLP模型(多层感知机)
导入模块 import hashlib import os import tarfile import zipfile import requests import numpy as np import pandas as pd import mindspore import mindspore.dataset as ds from mindspore import nn import mindspore.ops as ops import mindspore.numpy as mnp from …...
【论文极速读】VQ-VAE:一种稀疏表征学习方法
【论文极速读】VQ-VAE:一种稀疏表征学习方法 FesianXu 20221208 at Baidu Search Team 前言 最近有需求对特征进行稀疏编码,看到一篇论文VQ-VAE,简单进行笔记下。如有谬误请联系指出,本文遵循 CC 4.0 BY-SA 版权协议,…...
Flask-Blueprint
Flask-Blueprint 一、简介 概念: Blueprint 是一个存储操作方法的容器,这些操作在这个Blueprint 被注册到一个应用之后就可以被调用,Flask 可以通过Blueprint来组织URL以及处理请求 。 好处: 其本质上来说就是让程序更加松耦合…...
png图片转eps格式
下载latex工具后 在要转换的png图片文件夹路径下,打开命令行窗口,输入以下命令: bmeps -c fig图片名.png 图片名.eps...
English Learning - L2 语音作业打卡 Day2 2023.2.23 周四
English Learning - L2 语音作业打卡 Day2 2023.2.23 周四💌 发音小贴士:💌 当日目标音发音规则/技巧:🍭 Part 1【热身练习】🍭 Part2【练习内容】🍭【练习感受】🍓元音[ ɔ: ]&…...
低频量化之 可转债 配债 策略数据 - 全网独家
目录历史文章可转债配债数据待发转债(进展统计)待发转债(行业统计)待发转债(5证监会通过,PE排序)待发转债(5证监会通过,安全垫排序)待发转债(4发审…...
论文阅读_DALLE-2的unCLIP模型
论文信息 name_en: Hierarchical Text-Conditional Image Generation with CLIP Latents name_ch: 利用CLIP的层次化文本条件图像生成 paper_addr: http://arxiv.org/abs/2204.06125 doi: 10.48550/arXiv.2204.06125 date_read: 2023-02-12 date_publish: 2022-04-12 tags: [‘…...
软件测试5年,历经3轮面试成功拿下华为Offer,24K/16薪不过分吧
前言 转眼过去,距离读书的时候已经这么久了吗?,从18年5月本科毕业入职了一家小公司,到现在快5年了,前段时间社招想着找一个新的工作,前前后后花了一个多月的时间复习以及面试,前几天拿到了华为的…...
【软件工程】课程作业(三道题目:需求分析、概要设计、详细设计、软件测试)
文章目录:故事的开头总是极尽温柔,故事会一直温柔……💜一、你怎么理解需求分析?1、需求分析的定义:2、需求分析的重要性:3、需求分析的内容:4、基于系统分析的方法分类:5、需求分析…...
OpenClaw多模态扩展:结合百川2-13B-4bits与OCR的图像信息处理流程
OpenClaw多模态扩展:结合百川2-13B-4bits与OCR的图像信息处理流程 1. 为什么需要多模态能力扩展? 上周我需要整理一批技术文档的截图,包含代码片段、错误日志和流程图。手动转录不仅耗时,还容易出错。这让我开始思考:…...
番茄小说下载器:一站式离线阅读与听书解决方案
番茄小说下载器:一站式离线阅读与听书解决方案 【免费下载链接】Tomato-Novel-Downloader 番茄小说下载器不精简版 项目地址: https://gitcode.com/gh_mirrors/to/Tomato-Novel-Downloader 还在为网络不稳定而无法畅快阅读番茄小说烦恼吗?想要在通…...
Z-Image-GGUF在软件测试中的应用:自动化生成测试用例示意图
Z-Image-GGUF在软件测试中的应用:自动化生成测试用例示意图 你是不是也遇到过这样的场景?写测试用例文档时,为了描述一个复杂的用户操作流程,绞尽脑汁写了半天文字,结果评审时,开发同事还是没完全看懂&…...
终极LrcHelper歌词下载指南:5分钟学会网易云音乐双语歌词获取与设备适配
终极LrcHelper歌词下载指南:5分钟学会网易云音乐双语歌词获取与设备适配 【免费下载链接】LrcHelper 从网易云音乐下载带翻译的歌词 Walkman 适配 项目地址: https://gitcode.com/gh_mirrors/lr/LrcHelper 还在为找不到高质量双语歌词而烦恼吗?想…...
深度解析ShardingCore:EF Core分库分表架构实战与性能优化指南
深度解析ShardingCore:EF Core分库分表架构实战与性能优化指南 【免费下载链接】sharding-core high performance lightweight solution for efcore sharding table and sharding database support read-write-separation .一款ef-core下高性能、轻量级针对分表分库…...
把股票数据能力接进 AI:stock-sdk-mcp 的实践整理
起因 如果你经常用 Cursor、Claude 这类 AI 工具,应该已经能明显感觉到它们在通用问答和代码任务上越来越强了。但一旦问题变成金融数据查询,比如“看看贵州茅台今天的行情”“把最近 60 个交易日的日 K 线拉出来,再判断一下 MACD 和 RSI”&…...
FastAdmin定时任务实战:从数据库备份到邮件提醒的5个真实场景配置
FastAdmin定时任务实战:从数据库备份到邮件提醒的5个真实场景配置 在FastAdmin的实际开发中,定时任务就像一位不知疲倦的助手,能够自动完成各种重复性工作。但很多开发者掌握了基础配置后,却不知道如何将其应用到真实业务场景中。…...
结合AI改写技术与五个技巧,快速优化论文查重率至合格范围
嘿,大家好!我是AI菌。今天咱们来聊聊一个让无数学生头疼的问题:论文重复率飙到30%以上怎么办?别慌,我这就分享5个实用降重技巧,帮你一次搞定,轻松压到合格线以下。这些方法都是我亲身试验过的&a…...
掌握BepInEx:Unity游戏扩展全家桶的零门槛实践指南
掌握BepInEx:Unity游戏扩展全家桶的零门槛实践指南 【免费下载链接】BepInEx Unity / XNA game patcher and plugin framework 项目地址: https://gitcode.com/GitHub_Trending/be/BepInEx 🔍 游戏模组管理的行业痛点与解决方案 在Unity游戏生态…...
Android-Animation-Set转场动画实战:共享元素与Activity切换的完美结合
Android-Animation-Set转场动画实战:共享元素与Activity切换的完美结合 【免费下载链接】Android-Animation-Set :books: Android 所有动画系列详尽教程。 Explain all animations in Android. 项目地址: https://gitcode.com/gh_mirrors/an/Android-Animation-S…...
