skip list

news2024/9/21 22:19:34
无标题
#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]/head
    struct skip_list_node *next; // 下一个节点指针
} skip_list_item_t;

// 跳表节点
typedef struct skip_list_node
{
    skip_list_item_t item[SKIP_LIST_MAX_LEVEL]; // 跳表节点item
    uint32_t key;                                // 用于排序的key
    uint32_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.coloradmin.cn/o/2153441.html

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

相关文章

电子元器件之MOS管,附上几个常用MOS管电路和仿真。

MOS管是一种常用的电子元器件。 1.MOS管的类别 MOSFET简称MOS&#xff0c;是一种绝缘栅型场效应管。按照类别可以分为增强型mos管和耗尽型mos管。 导电沟道的形成方式‌ 增强型MOS管&#xff1a;在没有外加电压时&#xff0c;源极和漏极之间没有导电沟道存在。只有当栅极电…

【玉米田】

题目 代码 #include <bits/stdc.h> using namespace std; typedef long long LL;const int mod 1e8; const int M 1 << 12; LL f[13][M]; int g[13]; vector<int> state; vector<int> p[M]; int n, m; bool check(int x) {return !(x & x <&…

攻防世界---->Windows_Reverse1(补)

做题笔记。 做题回顾。 假设&#xff0c;我们不知道地址随机怎么办&#xff1f;不能动调&#xff0c;只能静态分析。 下载 查壳 upx脱壳。 32ida打开。 动调报错。 重新打开&#xff0c;静态分析。 跟进关键函数。 不明白可以反汇编和汇编一起看。 溯源。 *decode 取值等于 by…

分布式锁之 防误删(优化之UUID防误删)

文章目录 1、AlbumInfoApiController --》testLock()2、AlbumInfoServiceImpl --》testLock()3、问题&#xff1a;删除操作缺乏原子性。 实现如下&#xff1a; 1、AlbumInfoApiController --》testLock() Tag(name "专辑管理") RestController RequestMapping(&quo…

【计网】从零开始掌握序列化与反序列化 --- 基础知识储备与程序重构

从零开始掌握序列化与反序列化 1 初识序列化与反序列化2 再谈Tcp协议3 程序重构3.1 Socket类3.2 回调函数设计3.3 最终的Tcp服务器类 1 初识序列化与反序列化 在刚学习计算机网络时&#xff0c;我们谈到过网络协议栈&#xff0c;其中最上层的就是应用层&#xff0c;那么这个应…

Qt圆角窗口

Qt圆角窗口 问题&#xff1a;自己重写了一个窗口&#xff0c;发现用qss设置圆角了&#xff0c;但是都不生效&#xff0c;不过子窗口圆角都生效了。 无边框移动窗口 bool eventFilter(QObject *watched, QEvent *evt) {static QPoint mousePoint;static bool mousePressed f…

开源、极简的B站第三方,建议所有人收藏

很多人说B站落寞了&#xff0c;但我觉得不是B站落寞&#xff0c;而是长视频落寞了。现代人已经没有充足的耐心&#xff0c;刷完一个十分钟的视频。毕竟&#xff0c;短视频可以把这十分钟切成50份&#xff0c;让我们开心50次。 可怕的是&#xff0c;B站即使落寞&#xff0c;在长…

继承的例题

答案&#xff1a;D 解析&#xff1a;C允许一个子类继承多个父类 知识点&#xff1a; 子类是父类的特殊化&#xff0c;父类是子类的泛化 解析&#xff1a;子类可以共享父类的属性和方法&#xff0c;选项A正确 面向对象关系中&#xff0c;类与类的关系包含继承&#xff0c;包…

IntelliJ IDEA 2024.1.4 (Ultimate Edition)找不到Add Framework Support解决方法

目录 背景: 解决方法&#xff1a; 步骤1: 步骤2&#xff1a; 步骤3&#xff1a; 创建Web项目的完整流程&#xff1a; 步骤1: 步骤2: 步骤3&#xff1a; 步骤4&#xff1a; Web优点: 背景: 我的IDE版本是IntelliJ IDEA 2024.1.4 (Ultimate Edition)&#xff0c;当我…

【优选算法之双指针】No.2--- 经典双指针算法(下)

文章目录 前言一、双指针示例&#xff1a;1.1 ⽔果成篮1.2 和为s的两个数字1.3 三数之和1.4 四数之和 二、双指针总结&#xff1a; 前言 &#x1f467;个人主页&#xff1a;小沈YO. &#x1f61a;小编介绍&#xff1a;欢迎来到我的乱七八糟小星球&#x1f31d; &#x1f4cb;专…

前后端分离,使用MOCK进行数据模拟开发,让前端攻城师独立于后端进行开发

mock是什么 Mock生成随机数据,拦截Ajax 请求&#xff0c;前后端分离&#xff0c;让前端攻城师独立于后端进行开发。 增加单元测试的真实性 通过随机数据,模拟各种场景。 在实际开发过程中&#xff0c;前端是通过axios来请求数据的&#xff0c;很多时候前端开发者就是通过写固定…

【Git必看系列】—— Git巨好用的神器之git stash篇

应用场景 当我们开发一个新功能时会先从master拉出一个分支dev&#xff0c;然后在这个dev分支下吭哧吭哧的开始写代码开发新功能&#xff0c;就如下代码所示&#xff0c;我们在dev分支下开发Person类的新功能getId() public class Person {private int id;private String nam…

Vue3:v-model实现组件通信

目录 一.性质 1.双向绑定 2.语法糖 3.响应式系统 4.灵活性 5.可配置性 6.多属性绑定 7.修饰符支持 8.defineModel使用 二.使用 1.父组件 2.子组件 三.代码 1.父组件代码 2.子组件代码 四.效果 一.性质 在Vue3中&#xff0c;v-model指令的性质和作用主要体现在…

vue3 快速入门系列 —— 基础

基础 前面我们已经用 vue2 和 react 做过开发了。 AIAutoPrediction 从 vue2 升级到 vue3 成本较大&#xff0c;特别是较大的项目。所以许多公司对旧项目继续使用vue2&#xff0c;新项目则使用 vue3。 有些UI框架&#xff0c;比如ant design vue1.x 使用的 vue2。但现在 an…

5.C++面向对象2(类对象大小计算,结构体内存对齐,大小端存储方式,this指针)

⭐本篇文章为C学习第4章&#xff0c;主要了解类对象大小和this指针 ⭐本人C代码仓库&#xff1a;yzc的c学习: 小川c的学习记录 - Gitee.com 目录 一. 类对象模型 1.1 类成员函数和成员变量的分布 1.2 如何计算类的大小&#xff1f;&#xff08;结构体内存对齐&#xff09…

卸载node,安装nvm的详细使用方法

一、nvm是什么&#xff1f; nvm是一个node的版本管理工具&#xff0c;可以简单切换的node版本、安装、查看。。。等等&#xff0c;与npm不同的是&#xff0c;npm是依赖包的管理工具。 二、nvm下载安装 安装之前需要先把 自己电脑上边的node给卸载了&#xff0c;不然安装不好…

【数据结构初阶】链式二叉树接口实现超详解

文章目录 1. 节点定义2. 前中后序遍历2. 1 遍历规则2. 2 遍历实现2. 3 结点个数2. 3. 1 二叉树节点个数2. 3. 2 二叉树叶子节点个数2. 3. 3 二叉树第k层节点个数 2. 4 二叉树查找值为x的节点2. 5 二叉树层序遍历2. 6 判断二叉树是否是完全二叉树 3. 二叉树性质 1. 节点定义 用…

日志系统第三弹:日志消息和格式化模块的实现

日志系统第三弹&#xff1a;日志消息和格式化模块的实现 一、日志消息模块的实现二、日志格式化模块的设计1.格式化占位符的约定2.如何打印1.各种零件类1.抽象类2.简单的零件类3.日期零件类4.非格式化数据零件类 2.Formatter 3.如何解析 三、日志格式化模块的实现1.解析函数2.c…

一文详解Unity下RTMP推送|轻量级RTSP服务|RTSP|RTMP播放模块说明

技术背景 好多开发者&#xff0c;对Unity下的模块&#xff0c;不甚了解&#xff0c;实际上&#xff0c;除了Windows/Linux/Android/iOS Native SDK&#xff0c;大牛直播SDK发布了Unity环境下的RTMP推流|轻量级RTSP服务&#xff08;Windows平台Linux平台Android平台&#xff09…

Windows安装Oracle11gR2(图文教程)

本章教程&#xff0c;记录在Windows10上安装Oracle11gR2过程。 一、下载安装包 通过网盘分享的文件&#xff1a;oracle11g 链接: https://pan.baidu.com/s/15ilciQ5NlKWtClklmdAH_w?pwds4dd 提取码: s4dd 二、下载并解压文件 将网盘中的安装包文件下载到本地&#xff0c;在此之…