skip list

无标题
#include <iostream>
#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <ctime>#define SKIP_LIST_MAX_LEVEL 4// 跳表连接item
typedef struct skip_list_item
{struct skip_list_node *prev; // 上一个节点指针,指向头,&head[0]/headstruct skip_list_node *next; // 下一个节点指针
} skip_list_item_t;// 跳表节点
typedef struct skip_list_node
{skip_list_item_t item[SKIP_LIST_MAX_LEVEL]; // 跳表节点itemuint32_t key;                                // 用于排序的keyuint32_t level;                              // 节点当前所在层级
} skip_list_node_t;// 跳表结构
typedef struct skip_list
{skip_list_node_t head;  // 跳表每层的head,head.item[0]为原表uint64_t size;          // 跳表节点总数uint32_t level;         // 下次节点插入跳表层数uint32_t level_max;     // 跳表最大层数
} skip_list_t;/*******************************************************************************/
skip_list_t *skip_list_create()
{skip_list_t *skip_list = (skip_list_t *)calloc(1, sizeof(skip_list_t));skip_list->size = 0;skip_list->level = 0;skip_list->level_max = SKIP_LIST_MAX_LEVEL;return skip_list;
}void skip_list_destroy(skip_list_t *skip_list)
{skip_list_node_t *node = skip_list->head.item[0].next;while (node != NULL){skip_list_node_t *next = node->item[0].next;free(node);node = next;}free(skip_list);
}void skip_list_node_init(skip_list_node_t *node, uint32_t key, uint32_t level)
{memset(&node->item, 0, sizeof(node->item));node->key = key;node->level = level;
}void skip_list_update_level(skip_list_t *skip_list)
{uint32_t level = 1;while ((rand() & 65535) < 32768){level++;}level = level < SKIP_LIST_MAX_LEVEL ? level : SKIP_LIST_MAX_LEVEL;skip_list->level = level - 1;
}void skip_list_insert(skip_list_t *skip_list, skip_list_node_t *node)
{skip_list_update_level(skip_list);node->level = skip_list->level;skip_list_node_t *update[SKIP_LIST_MAX_LEVEL];skip_list_node_t *x = &skip_list->head;int i = SKIP_LIST_MAX_LEVEL - 1;// 找到每一层要插入的位置while (i >= 0){while (x->item[i].next != NULL && x->item[i].next->key < node->key){x = x->item[i].next;}update[i] = x;i--;}// 执行插入操作for (int i = 0; i <= (int)node->level; i++){node->item[i].next = update[i]->item[i].next;if (update[i]->item[i].next != NULL){update[i]->item[i].next->item[i].prev = node;}update[i]->item[i].next = node;node->item[i].prev = update[i];}skip_list->size++;
}skip_list_node_t *skip_list_find(skip_list_t *skip_list, uint32_t key)
{if (skip_list->size == 0)return NULL; // 空表skip_list_node_t *x = &skip_list->head;int i = SKIP_LIST_MAX_LEVEL - 1;// 先找到节点while (i >= 0){while (x->item[i].next != NULL && x->item[i].next->key < key){x = x->item[i].next;}i--;}x = x->item[0].next;if (x != NULL && x->key == key){return x;}return NULL;
}skip_list_node_t *skip_list_find_max(skip_list_t *skip_list)
{if (skip_list->size == 0)return NULL; // 空表skip_list_node_t *x = &skip_list->head;int i = SKIP_LIST_MAX_LEVEL - 1;// 找到最大节点while (i >= 0){while (x->item[i].next != NULL){x = x->item[i].next;}i--;}if (x == &skip_list->head)return NULL;return x;
}skip_list_node_t *skip_list_find_min(skip_list_t *skip_list)
{if (skip_list->size == 0)return NULL; // 空表skip_list_node_t *x = skip_list->head.item[0].next;if (x == NULL)return NULL;return x;
}void skip_list_print(skip_list_t *skip_list)
{for (int i = SKIP_LIST_MAX_LEVEL - 1; i >= 0; i--){skip_list_node_t *x = skip_list->head.item[i].next;std::cout << "Level " << i << ": ";while (x != NULL){std::cout << x->key << " ";x = x->item[i].next;}std::cout << std::endl;}
}int main()
{skip_list_t *skip_list = skip_list_create();srand(time(NULL));uint32_t keys[] = {3, 6, 7, 9, 12, 19, 17, 26, 21, 25};for (uint32_t key : keys){skip_list_node_t *node = (skip_list_node_t *)malloc(sizeof(skip_list_node_t));skip_list_node_init(node, key, 0);skip_list_insert(skip_list, node);}std::cout << "Skip List:" << std::endl;skip_list_print(skip_list);uint32_t search_key = 19;skip_list_node_t *found = skip_list_find(skip_list, search_key);std::cout << "Search for " << search_key << ": " << (found ? "Found" : "Not Found") << std::endl;search_key = 15;found = skip_list_find(skip_list, search_key);std::cout << "Search for " << search_key << ": " << (found ? "Found" : "Not Found") << std::endl;skip_list_destroy(skip_list);return 0;
} 

skip_list_insert 函数流程

随机决定新节点的层数

void skip_list_insert(skip_list_t *skip_list, skip_list_node_t *node)
{skip_list_update_level(skip_list);  // 随机更新跳表的层数node->level = skip_list->level;     // 设置新节点的层数
}

skip_list_update_level(skip_list) 会随机决定新节点需要占据的层数(根据概率,高层节点少,低层节点多,类似于抛硬币)。
新节点 node 的层数被设置为刚刚生成的随机层数 skip_list->level。

找到每一层要插入的位置

skip_list_node_t *update[SKIP_LIST_MAX_LEVEL];  // 保存插入位置的前驱节点
skip_list_node_t *x = &skip_list->head;         // 从跳表的头节点开始
int i = SKIP_LIST_MAX_LEVEL - 1;// 找到每一层要插入的位置
while (i >= 0)
{while (x->item[i].next != NULL && x->item[i].next->key < node->key){x = x->item[i].next;  // 在第 i 层向右移动,寻找插入位置}update[i] = x;  // 记录需要在第 i 层插入的位置的前一个节点i--;  // 进入下一层
}skip_list_node_t *update[SKIP_LIST_MAX_LEVEL];  // 保存插入位置的前驱节点
skip_list_node_t *x = &skip_list->head;         // 从跳表的头节点开始
int i = SKIP_LIST_MAX_LEVEL - 1;// 找到每一层要插入的位置
while (i >= 0)
{while (x->item[i].next != NULL && x->item[i].next->key < node->key){x = x->item[i].next;  // 在第 i 层向右移动,寻找插入位置}update[i] = x;  // 记录需要在第 i 层插入的位置的前一个节点i--;  // 进入下一层
}


初始化了一个 update 数组,用于保存插入位置前驱节点。
设置一个指针 x 指向跳表的头节点,从最高层 (i = SKIP_LIST_MAX_LEVEL - 1) 开始。
通过内层循环,在每一层查找当前节点 x 所需插入位置的前一个节点,并将该节点记录到 update 数组。
如果 x->item[i].next 不为空且 x->item[i].next->key 小于 node->key,则 x 继续向右移动。
一旦找到适合插入位置的节点,记录在 update[i] 中。
逐层向下(i–)重复上述步骤,直到最低层(层0)。
3. 执行插入操作
// 执行插入操作
for (int i = 0; i <= (int)node->level; i++)
{
node->item[i].next = update[i]->item[i].next; // 新节点的 next 指向前驱节点的 next
if (update[i]->item[i].next != NULL)
{
update[i]->item[i].next->item[i].prev = node; // 前驱节点的 next 指向新节点
}
update[i]->item[i].next = node; // 前驱节点的 next 指向新节点
node->item[i].prev = update[i]; // 新节点的 prev 指向前驱节点
}

skip_list->size++;  // 更新跳表中的节点数

}
复制
// 执行插入操作
for (int i = 0; i <= (int)node->level; i++)
{
node->item[i].next = update[i]->item[i].next; // 新节点的 next 指向前驱节点的 next
if (update[i]->item[i].next != NULL)
{
update[i]->item[i].next->item[i].prev = node; // 前驱节点的 next 指向新节点
}
update[i]->item[i].next = node; // 前驱节点的 next 指向新节点
node->item[i].prev = update[i]; // 新节点的 prev 指向前驱节点
}

skip_list->size++;  // 更新跳表中的节点数

}

遍历从第0层到新节点的最大层 node->level:
node->item[i].next = update[i]->item[i].next;:新节点 node 的下一个节点指向更新节点 update[i] 的下一个节点。
if (update[i]->item[i].next != NULL):如果 update[i]->item[i].next 不是空,则更新下一个节点的 prev 指针指向新节点 node。
update[i]->item[i].next = node;:更新节点 update[i] 的下一个节点指向新节点 node。
node->item[i].prev = update[i];:新节点 node 的前一个节点指向更新节点 update[i]。
最后,更新跳表的节点总数 skip_list->size++。
插入过程示意图
假设跳表如下,准备插入节点 node(key为10):

当前跳表:
Level 3: head -> 15 -> NULL
Level 2: head -> 9 -> 15 -> NULL
Level 1: head -> 7 -> 9 -> 15 -> NULL
Level 0: head -> 3 -> 7 -> 9 -> 15 -> NULL
复制
当前跳表:
Level 3: head -> 15 -> NULL
Level 2: head -> 9 -> 15 -> NULL
Level 1: head -> 7 -> 9 -> 15 -> NULL
Level 0: head -> 3 -> 7 -> 9 -> 15 -> NULL

  1. 查找插入位置:
    在每一层中找到应插入位置前的节点。

update[] = { head, 7, 9, 9 };
复制
update[] = { head, 7, 9, 9 };

2. 执行插入:
假设新节点随机生成的层数为2(第三层也有节点)。

把新节点插入适当位置后:

插入 key=10 后的跳表:
Level 3: head -> 10 -> 15 -> NULL
Level 2: head -> 9 -> 10 -> 15 -> NULL
Level 1: head -> 7 -> 9 -> 10 -> 15 -> NULL
Level 0: head -> 3 -> 7 -> 9 -> 10 -> 15 -> NULL
复制
插入 key=10 后的跳表:
Level 3: head -> 10 -> 15 -> NULL
Level 2: head -> 9 -> 10 -> 15 -> NULL
Level 1: head -> 7 -> 9 -> 10 -> 15 -> NULL
Level 0: head -> 3 -> 7 -> 9 -> 10 -> 15 -> NULL

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

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

相关文章

群晖NAS使用Docker本地部署网页版Ubuntu系统并实现无公网IP远程访问

文章目录 前言1. 下载Docker-Webtop镜像2. 运行Docker-Webtop镜像3. 本地访问网页版Linux系统4. 群晖NAS安装Cpolar工具5. 配置异地访问Linux系统6. 异地远程访问Linux系统7. 固定异地访问的公网地址 前言 本文旨在详细介绍如何在群晖NAS部署docker-webtop&#xff0c;并结合c…

电力电塔电线缺陷检测数据集 voc yolo

电力 电塔电线缺陷检测数据集 10000张 带标注 voc yolo 电力电塔电线缺陷检测数据集 数据集描述 该数据集旨在用于电力电塔和电线的缺陷检测任务&#xff0c;涵盖多种常见的缺陷类型。数据集包含了大量的图像及其对应的标注信息&#xff0c;可用于训练计算机视觉模型&#x…

Linux:进程(一)

目录 一、概念的理解 二、指令操作 一、概念的理解 在许多地方对进程的定义都是这样的一句话&#xff1a;加载到内存中的程序、正在运行的程序、进程可以排队 而要真正理解进程是什么&#xff0c;这一句话的解释远远不够。 在定义进程之前&#xff0c;先来理解程序&#xff0…

21、Tomato

难度 低(个人认为中) 目标 root权限 一个flag 使用VMware启动 kali 192.168.152.56 靶机 192.168.152.66 信息收集 端口信息收集 可以看到有个ftp服务&#xff0c;2211实际是ssh协议端口&#xff0c;80、8888是一个web服务 web测试 80端口显示一个tomato 查看源码给了一些…

MATLAB系列06:复数数据、字符数据和附加画图类

MATLAB系列06&#xff1a;复数数据、字符数据和附加画图类 6. 复数数据、字符数据和附加画图类6.1 复数数据6.1.1 复变量&#xff08; complex variables&#xff09;6.1.2 带有关系运算符的复数的应用6.1.3 复函数&#xff08; complex function&#xff09;6.1.4 复数数据的作…

【FPGA】编程方式

FPGA编程方式 1 什么是PLD&#xff1f;2 什么是颗粒度&#xff1f;3 可编程逻辑器件的编程方式有哪些&#xff1f;3.1 SRAM 编程技术3.2 Flash/EEPROM 编程技术3.3 反熔丝编程技术3.4 编程技术比较 参考资料 1 什么是PLD&#xff1f; 可编程逻辑器件 英文全称为&#xff1a;pr…

SpringBoot 数据库表结构文档生成

官方地址&#xff1a;https://github.com/pingfangushi/screw screw 螺丝钉&#xff0c;支持以下数据库 MySQL MariaDB TIDB Oracle SqlServer PostgreSQL Cache DB&#xff08;2016&#xff09; 生产文档支持 html word markdown 开始 添加依赖 <!-- 螺丝钉 --><…

c语言面试字符串复制

1&#xff0c;下面这个函数的打印是什么&#xff1a; #include<stdio.h> #include<string.h>int main() {char str0[5], str1[] "welcome";strcpy(str0, str1);printf("str0:%s\r\n",str0);printf("str1:%s\r\n",str1); } larkla…

nginx实现https安全访问的详细配置过程

文章目录 前言什么是 HTTP&#xff1f;什么是 HTTPS&#xff1f;HTTP 和 HTTPS 的区别为什么 HTTPS 被称为安全的&#xff1f;配置过程配置自签名证书 前言 首先我们来简单了解一下什么是http和https以及他们的区别所在. 什么是 HTTP&#xff1f; HTTP&#xff0c;全称为“超…

notepad++的json查看

json文件查看 因为接触到3dtile模型&#xff0c;所以经常需要和json打交道&#xff0c;但是很多模型是下面这种情况&#xff0c;不好阅读&#xff0c;所以可以使用notepad的插件查看 正常打开是这样的 加载notepad插件 搜索json下载安装就可以了 如果网络抽象&#xff0c;下载…

Hive企业级调优[3]—— Explain 查看执行计划

Explain 查看执行计划 Explain 执行计划概述 EXPLAIN 命令呈现的执行计划由一系列 Stage 组成。这些 Stage 之间存在依赖关系&#xff0c;每一个 Stage 可能对应一个 MapReduce Job 或者一个文件系统的操作等。如果某 Stage 对应了一个 MapReduce Job&#xff0c;则该 Job 在 …

Apache James配置连接达梦数据库

项目场景&#xff1a; Apache James配置连接达梦数据库&#xff0c;其他配置中不存在的数据库也可参考此方案。 配置步骤 1、把需要的jar包导入到James 把DmJdbcDriver18.jar复制到下面lib目录下 james-2.3.2\lib 2、 修改连接配置 james-2.3.2\apps\james\SAR-INF\confi…

C# 技巧在 foreach 循环中巧妙获取索引

目录 前言 使用 LINQ 和扩展方法 直接在 LINQ 查询中使用 使用 LINQ 的 Select() 与 Enumerable.Range() 总结 最后 前言 在C#中foreach 循环是处理集合的常见方式&#xff0c;因其简洁性和易读性而广受青睐。 但是在某些情况下&#xff0c;我们需要同时获取集合中元素的…

[深度学习]神经网络

1 人工神经网络 全连接神经网络 2 激活函数 隐藏层激活函数由人决定输出层激活函数由解决的任务决定: 二分类:sigmoid多分类:softmax回归:不加激活(恒等激活identify)2.1 sigmoid激活函数 x为加权和小于-6或者大于6,梯度接近于0,会出现梯度消失的问题即使取值 [-6,6] ,…

头戴式蓝牙耳机性价比高的有哪些?四款高能性价比机型对比推荐

在当今科技日新月异的时代&#xff0c;头戴式蓝牙耳机已经成为了我们日常生活中不可或缺的一部分&#xff0c;无论是通勤路上、健身房内还是家中休闲时&#xff0c;一副优质的头戴式蓝牙耳机都能为我们带来沉浸式的听觉体验&#xff0c;那么头戴式蓝牙耳机性价比高的有哪些&…

模版结构体没有可用成员(C3203)

没有typedef模版结构体而导致。 并且_tables[index]无法访问HashData内部的成员。

Windows系统使用PHPStudy搭建Cloudreve私有云盘公网环境远程访问

文章目录 1、前言2、本地网站搭建2.1 环境使用2.2 支持组件选择2.3 网页安装2.4 测试和使用2.5 问题解决 3、本地网页发布3.1 cpolar云端设置3.2 cpolar本地设置 4、公网访问测试5、结语 1、前言 自云存储概念兴起已经有段时间了&#xff0c;各互联网大厂也纷纷加入战局&#…

Vue(13)——router-link

router-link vue-router提供了一个全局组件router-link(取代a标签) 能跳转&#xff0c;配置to属性指定路径&#xff08;必须&#xff09;。本质还是a标签。默认会提供高亮类名&#xff0c;可以直接设置高亮样式 右键检查&#xff0c;发现多了两个类&#xff1a; 可以直接写样式…

【Python】探索 Errbot:多功能聊天机器人框架

不是旅行治愈了你&#xff0c;是你在路上放过了自己。 在当今的数字化时代&#xff0c;聊天机器人已成为企业与客户互动、提升工作效率和增加乐趣的重要工具。Errbot是一个高度可扩展的聊天机器人框架&#xff0c;它允许开发者使用Python轻松创建和定制机器人。本文将介绍Errb…

大型语言模型 (LLM) 劫持攻击不断升级,导致每天损失超过 100,000 美元

Sysdig 威胁研究团队 (TRT) 报告称&#xff0c;LLMjacking&#xff08;大型语言模型劫持&#xff09;事件急剧增加&#xff0c;攻击者通过窃取的云凭证非法访问大型语言模型 (LLM)。 这一趋势反映了 LLM 访问黑市的不断增长&#xff0c;攻击者的动机包括个人使用和规避禁令和制…