数据结构与算法—跳表(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、需求分析…...
Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...
2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
