【数据结构】线性表(五)跳表及其基本操作(定义、创建、查找、插入、删除)

news2024/11/16 15:43:25

目录

前言

1. 单链表

跳表(Skip List)

0. 概念

1. 数据结构

a. 跳表节点结构SkipListNode

b. 跳表结构SkipList

2. 辅助函数

a. 初始化节点

b. 初始化跳表

c. 生成随机层数

3. 查找节点

4. 插入节点

5. 删除节点

6. 主函数

代码整合


前言

1. 单链表

        链表中的结点用存储单元(若干个连续字节)来存放,存储单元之间既可以是(存储空间上)连续的,也可以是不连续的,甚至可以零散地分布在存储空间中的任何位置。换言之,链表中结点的逻辑次序和物理次序之间并无必然联系。最重要的是,链表可以在不移动结点位置的前提下根据需要随时添加删除结点,动态调整。

【数据结构】线性表(二)单链表及其基本操作(创建、插入、删除、修改、遍历打印)-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_63834988/article/details/133914875

数据结构_QomolangmaH的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_63834988/category_12435965.html

跳表(Skip List)

0. 概念

        增加了向前指针的链表叫作跳表。跳表全称叫做跳跃表,简称跳表。跳表是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表。跳表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找。跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能。

        跳表是一个随机化的数据结构,可以被看做二叉树的一个变种,它的性能在某些情况下可以与平衡二叉树(如红黑树和AVL树)相媲美它的查找、插入和删除操作的时间复杂度为O(log n)

        跳表的原理相对简单,因此在一些具有内存限制或对插入和删除操作性能要求较高的场景下,跳表是一种常用的选择。例如,Redis和LevelDB等数据库系统中就广泛使用了跳表来实现有序集合的功能。

1. 数据结构

定义宏:

#define MAX_LEVEL 6

a. 跳表节点结构SkipListNode

typedef struct SkipListNode {
    int key;
    int value;
    struct SkipListNode** forward;
} SkipListNode;

      SkipListNode表示跳表中的节点,包含一个整型的键值key和一个整型的值value,以及一个指向其他节点的指针数组forward

b. 跳表结构SkipList

typedef struct SkipList {
    int max_level;
    int level;
    SkipListNode* header;
} SkipList;

    SkipList表示整个跳表,包含最大层数max_level、当前层数level和一个指向头节点的指针header

2. 辅助函数

a. 初始化节点

SkipListNode* skipListNodeInit(int level, int key, int value) {
    SkipListNode* node = (SkipListNode*)malloc(sizeof(SkipListNode));
    node->key = key;
    node->value = value;
    node->forward = (SkipListNode**)malloc((level + 1) * sizeof(SkipListNode*));
    for (int i = 0; i <= level; i++) {
        node->forward[i] = NULL;
    }
    return node;
}
  • 接受节点的层数level、键值key和值value作为参数,并动态分配内存来创建节点;
  • 将节点的键值和值设置为参数的值;
  • 为指针数组forward分配足够的内存;
  • 将指针数组中的所有元素初始化为NULL,并返回创建的节点。

b. 初始化跳表

SkipList* skipListInit() {
    SkipList* skipList = (SkipList*)malloc(sizeof(SkipList));
    skipList->max_level = MAX_LEVEL;
    skipList->level = 0;
    skipList->header = skipListNodeInit(MAX_LEVEL, 0, 0);
    for (int i = 0; i <= MAX_LEVEL; i++) {
        skipList->header->forward[i] = NULL;
    }
    return skipList;
}
  • 动态分配内存来创建一个SkipList结构体
  • 设置最大层数max_level为预定义的常量值MAX_LEVEL
  • 设置当前层数level为0
  • 调用上述skipListNodeInit函数创建一个头节点。
  • 将头节点的指针数组中的所有元素初始化为NULL,并返回创建的跳表。

c. 生成随机层数

int randomLevel() {
    int level = 1;
    while (rand() < RAND_MAX / 2 && level < MAX_LEVEL) {
        level++;
    }
    return level;
}
  • 使用rand函数生成一个伪随机数,并与RAND_MAX的一半进行比较。如果生成的数小于RAND_MAX的一半,并且层数小于最大层数MAX_LEVEL,则层数加1。
  • 函数的目的是根据一定的概率生成一个合适的层数,用于插入节点时确定节点在每一层的高度。

3. 查找节点

SkipListNode* skipListSearch(SkipList* skipList, int key) {
    SkipListNode* current = skipList->header;
    for (int i = skipList->level; i >= 0; i--) {
        while (current->forward[i] != NULL && current->forward[i]->key < key) {
            current = current->forward[i];
        }
    }
    current = current->forward[0];
    if (current != NULL && current->key == key) {
        return current;
    } else {
        return NULL;
    }
}

        从跳表的最高层开始遍历节点,找到在每一层中键值小于给定键值的最大节点。然后,沿着最底层的指针找到当前节点,并检查是否存在具有相同键值的节点。如果存在,返回该节点的指针。否则,返回NULL表示未找到。

4. 插入节点

void skipListInsert(SkipList* skipList, int key, int value) {
    SkipListNode* update[MAX_LEVEL + 1];
    SkipListNode* current = skipList->header;
    for (int i = skipList->level; i >= 0; i--) {
        while (current->forward[i] != NULL && current->forward[i]->key < key) {
            current = current->forward[i];
        }
        update[i] = current;
    }
    current = current->forward[0];
    if (current != NULL && current->key == key) {
        current->value = value;
    } else {
        int new_level = randomLevel();
        if (new_level > skipList->level) {
            for (int i = skipList->level + 1; i <= new_level; i++) {
                update[i] = skipList->header;
            }
            skipList->level = new_level;
        }
        SkipListNode* new_node = skipListNodeInit(new_level, key, value);
        for (int i = 0; i <= new_level; i++) {
            new_node->forward[i] = update[i]->forward[i];
            update[i]->forward[i] = new_node;
        }
    }
}
  • 创建一个更新数组update,用于记录在每个层级上需要更新的节点
  • 从最高层级开始逐层遍历,找到要插入位置的前一个节点,并将其记录在更新数组中。
  • 通过比较当前节点的后继节点的键和要插入的键,确定新节点的插入位置,并更新前进节点数组。
    • 如果要插入的键已存在于跳表中,则更新对应的值。
    • 如果新节点的层级大于当前跳表的层级,需要更新跳表的层级,并更新对应层级的前进节点数组。最后,将新节点插入到跳表中。

5. 删除节点

void skipListDelete(SkipList* skipList, int key) {
    SkipListNode* update[MAX_LEVEL + 1];
    SkipListNode* current = skipList->header;
    for (int i = skipList->level; i >= 0; i--) {
        while (current->forward[i] != NULL && current->forward[i]->key < key) {
            current = current->forward[i];
        }
        update[i] = current;
    }
    current = current->forward[0];
    if (current != NULL && current->key == key) {
        for (int i = 0; i <= skipList->level; i++) {
            if (update[i]->forward[i] != current) {
                break;
            }
            update[i]->forward[i] = current->forward[i];
        }
        free(current);
        while (skipList->level > 0 && skipList->header->forward[skipList->level] == NULL) {
            skipList->level--;
        }
    }
}
  • 创建一个更新数组update,用于记录在每个层级上需要更新的节点。
  • 从最高层级开始逐层遍历,找到要删除的节点的前一个节点,并将其记录在更新数组中。
  • 通过比较当前节点的后继节点的键和要删除的键,确定要删除的节点,并更新前进节点数组。
  • 释放要删除的节点的内存。
  • 检查跳表的当前层数,如果有必要,将当前层数减少,以确保跳表的高度与节点数量相匹配。

6. 主函数

int main() {
    srand(time(NULL));
    SkipList* skipList = skipListInit();

    skipListInsert(skipList, 3, 30);
    skipListInsert(skipList, 1, 10);
    skipListInsert(skipList, 2, 20);
    skipListInsert(skipList, 4, 40);
    skipListInsert(skipList, 6, 60);
    skipListInsert(skipList, 5, 50);
    skipListInsert(skipList, 7, 70);

    SkipListNode* node = skipListSearch(skipList, 4);
    if (node != NULL) {
        printf("Key: %d, Value: %d\n", node->key, node->value);
    } else {
        printf("Key not found.\n");
    }

    skipListDelete(skipList, 4);

    node = skipListSearch(skipList, 4);
    if (node != NULL) {
        printf("Key: %d, Value: %d\n", node->key, node->value);
    } else {
        printf("Key not found.\n");
    }

    return 0;
}
  • 调用srand函数设置随机数种子
  • 通过调用skipListInit函数初始化一个跳表
  • 使用skipListInsert函数插入一系列键值对
  • 使用skipListSearch函数搜索具有键值为4的节点,并打印出节点的键值和值。
  • 使用skipListDelete函数删除具有键值为4的节点。
  • 再次使用skipListSearch函数搜索具有键值为4的节点,并打印出结果。

代码整合

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define MAX_LEVEL 6

typedef struct SkipListNode {
    int key;
    int value;
    struct SkipListNode** forward;
} SkipListNode;

typedef struct SkipList {
    int max_level;
    int level;
    SkipListNode* header;
} SkipList;

SkipListNode* skipListNodeInit(int level, int key, int value) {
    SkipListNode* node = (SkipListNode*)malloc(sizeof(SkipListNode));
    node->key = key;
    node->value = value;
    node->forward = (SkipListNode**)malloc((level + 1) * sizeof(SkipListNode*));
    for (int i = 0; i <= level; i++) {
        node->forward[i] = NULL;
    }
    return node;
}

SkipList* skipListInit() {
    SkipList* skipList = (SkipList*)malloc(sizeof(SkipList));
    skipList->max_level = MAX_LEVEL;
    skipList->level = 0;
    skipList->header = skipListNodeInit(MAX_LEVEL, 0, 0);
    for (int i = 0; i <= MAX_LEVEL; i++) {
        skipList->header->forward[i] = NULL;
    }
    return skipList;
}

int randomLevel() {
    int level = 1;
    while (rand() < RAND_MAX / 2 && level < MAX_LEVEL) {
        level++;
    }
    return level;
}

void skipListInsert(SkipList* skipList, int key, int value) {
    SkipListNode* update[MAX_LEVEL + 1];
    SkipListNode* current = skipList->header;
    for (int i = skipList->level; i >= 0; i--) {
        while (current->forward[i] != NULL && current->forward[i]->key < key) {
            current = current->forward[i];
        }
        update[i] = current;
    }
    current = current->forward[0];
    if (current != NULL && current->key == key) {
        current->value = value;
    } else {
        int new_level = randomLevel();
        if (new_level > skipList->level) {
            for (int i = skipList->level + 1; i <= new_level; i++) {
                update[i] = skipList->header;
            }
            skipList->level = new_level;
        }
        SkipListNode* new_node = skipListNodeInit(new_level, key, value);
        for (int i = 0; i <= new_level; i++) {
            new_node->forward[i] = update[i]->forward[i];
            update[i]->forward[i] = new_node;
        }
    }
}

void skipListDelete(SkipList* skipList, int key) {
    SkipListNode* update[MAX_LEVEL + 1];
    SkipListNode* current = skipList->header;
    for (int i = skipList->level; i >= 0; i--) {
        while (current->forward[i] != NULL && current->forward[i]->key < key) {
            current = current->forward[i];
        }
        update[i] = current;
    }
    current = current->forward[0];
    if (current != NULL && current->key == key) {
        for (int i = 0; i <= skipList->level; i++) {
            if (update[i]->forward[i] != current) {
                break;
            }
            update[i]->forward[i] = current->forward[i];
        }
        free(current);
        while (skipList->level > 0 && skipList->header->forward[skipList->level] == NULL) {
            skipList->level--;
        }
    }
}

SkipListNode* skipListSearch(SkipList* skipList, int key) {
    SkipListNode* current = skipList->header;
    for (int i = skipList->level; i >= 0; i--) {
        while (current->forward[i] != NULL && current->forward[i]->key < key) {
            current = current->forward[i];
        }
    }
    current = current->forward[0];
    if (current != NULL && current->key == key) {
        return current;
    } else {
        return NULL;
    }
}

int main() {
    srand(time(NULL));
    SkipList* skipList = skipListInit();

    skipListInsert(skipList, 3, 30);
    skipListInsert(skipList, 1, 10);
    skipListInsert(skipList, 2, 20);
    skipListInsert(skipList, 4, 40);
    skipListInsert(skipList, 6, 60);
    skipListInsert(skipList, 5, 50);
    skipListInsert(skipList, 7, 70);

    SkipListNode* node = skipListSearch(skipList, 4);
    if (node != NULL) {
        printf("Key: %d, Value: %d\n", node->key, node->value);
    } else {
        printf("Key not found.\n");
    }

    skipListDelete(skipList, 4);

    node = skipListSearch(skipList, 4);
    if (node != NULL) {
        printf("Key: %d, Value: %d\n", node->key, node->value);
    } else {
        printf("Key not found.\n");
    }

    return 0;
}

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

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

相关文章

Java基础--》做个简易的计算器

使用多态实现计算器的加减乘除&#xff0c;根据运算符不同实例化不同子类进行计算&#xff08;运算符可键盘接收输入&#xff09; 例如&#xff1a;加法有num1、num2属性&#xff0c;方法&#xff1a;计算求和减法有num1、num2属性&#xff0c;方法&#xff1a;计算求差乘法有…

【Java异常】什么是异常,Java中如何处理异常?

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ Java异常处理 1. 了解异常&#xff1a;2. 异常…

人大金仓与哪吒科技达成战略合作,加快推动智慧港口建设

近日&#xff0c;人大金仓与哪吒港航智慧科技&#xff08;上海&#xff09;有限公司&#xff08;以下简称“哪吒科技”&#xff09;达成战略合作。双方旨在共享优势资源&#xff0c;联合为港口企业转型升级提供完备的技术支撑与行业解决方案。人大金仓总裁杜胜、哪吒科技总经理…

财务系统的报账页面设计

报账&#xff0c;指各部门向财务部报送账单&#xff0c;财务审批后予以结算。 报账的做法&#xff0c;小公司可能会少一些&#xff0c;大公司会多一些。小公司人员少&#xff0c;沟通快捷&#xff0c;运转效率高&#xff0c;老板予以口头肯定后财务直接付款了。大公司的部门很多…

wpf主页面解析

1、 开头的网址作用 1和2都是引入命名空间的&#xff0c;每一个字符串代表一系列的命名空间&#xff0c;这样就可以不用一个一个引用了。wpf中规定有一个名称空间是可以不加名字的&#xff0c;xmlns不加名字是默认命名空间。 "http://schemas.microsoft.com/winfx/2006/x…

如何利用开源考试系统进行在线远程考试?

在当前全球疫情的影响下&#xff0c;远程教育和在线考试的需求日益增长。开源考试系统成为一种受欢迎的选择&#xff0c;它为教师和学生提供了便利的远程考试解决方案。 选择适合自己需求的开源考试系统是至关重要的。多种开源考试系统在市场上可供选择。教师应根据自己的教学…

海外版知乎Quora,如何使用Quora进行营销?

想必大家对知乎非常熟悉&#xff0c;而Quora作为海外最受欢迎的网站之一&#xff0c;是与知乎功能与性质非常相似的一个平台&#xff0c;靠回答别人的问题获得关注&#xff0c;是引流最快的一个平台。对于做跨境电商、独立站的商家来说&#xff0c;这是一个绝佳的免费引流广告工…

ABAP程序不报错缺出错---解决

ALV字段名不显示 自建的透明表 REPORT ZTXYY_1123. DATA: gr_alv TYPE REF TO cl_salv_table,gr_columns TYPE REF TO cl_salv_columns_table. DATA: ZPL_LIST TYPE TABLE OF ZPL_EINVOICE_LOG. CALL METHOD cl_salv_table>factory IMPORTINGr_salv_table gr_alv CHAN…

点云处理【四】(点云关键点检测)

第一章 点云数据采集 第二章 点云滤波 第二章 点云降采样 1.点云关键点是什么&#xff1f; 关键点也称为兴趣点&#xff0c;它是2D图像、3D点云或曲面模型上&#xff0c;可以通过定义检测标准来获取的具有稳定性、区别性的点集。 我们获得的数据量大&#xff0c;特别是几十万…

C++模拟实现——list

一、成员变量及其基本结构 1.基本结构模型 本质是一个带头双向循环列表&#xff0c;将节点进行封装&#xff0c;并且为了方便使用&#xff0c;进行重定义 2.节点的封装定义 template<class T>//定义节点struct list_node{list_node<T>* _prev;list_node<T>…

linux性能分析(三)CPU篇(一)基础

一 CPU篇 遗留&#xff1a; 负载与cpu关系、负载与线程的关系? ① CPU 相关概念 1、physical 物理CPU个数 --> 一般一个实体 2、cpu 核数 3、逻辑CPU个数 逻辑核 4、超线程 super thread 技术 5、各种cpu的计算方式物理 physical CPU的个数&#xff1a; physical id逻…

易点易动固定资产管理系统引入RFID手持终端助力固定资产盘点

在现代商业环境中&#xff0c;固定资产盘点和管理对企业的运营至关重要。然而&#xff0c;传统的手工盘点方法已经无法满足企业对效率和准确性的要求。为了解决这一问题&#xff0c;易点易动固定资产管理系统引入RFID&#xff08;射频识别&#xff09;手持终端&#xff0c;为固…

北京卫视《为你喝彩》——星辰天合 CEO 胥昕,他专攻 SDS 让“数据常青”

10 月 18 日晚&#xff0c;北京卫视《为你喝彩》栏目播出&#xff0c;主题为《你有没有为梦想拼过命&#xff1f;听创业者说》&#xff0c;星辰天合 CEO 胥昕作为主人公之一&#xff0c;讲述了自己的创业故事。 如下内容摘自北京卫视&#xff1a; 青春总有着万般姿态&#xf…

图数据库实践 - 如何将图数据库应用于对公信贷

导读 日前&#xff0c;在经济形势和政策环境下&#xff0c;银行信贷结构进一步调整&#xff0c;民营企业、小微企业、绿色信贷投放力度持续加大。而坚持金融服务实体经济是政府的一贯主张&#xff0c;据相关工作报告指出&#xff0c;要求用好普惠小微贷款支持工具&#xff0c;…

为什么需要协调能力?如何提高协调能力?

协调能力指的是协作与调和&#xff0c;属于综合性能力的体现&#xff0c;涉及到表达&#xff0c;沟通&#xff0c;逻辑等方面&#xff0c;在日常生活中缺乏协调能力&#xff0c;也许影响并不太大&#xff0c;但是如果在职业发展中&#xff0c;协调能力就尤为重要&#xff0c;尤…

【公益案例展】广碳所——恒生电子基于区块链技术打造区域性碳中和登记系统...

‍ 恒生电子公益案例 本项目案例由恒生电子投递并参与数据猿与上海大数据联盟联合推出的 #榜样的力量# 《2023中国数据智能产业最具社会责任感企业》榜单/奖项”评选。 ‍数据智能产业创新服务媒体 ——聚焦数智 改变商业 全球气候变暖、温室效应明显、二氧化碳排放增多&#…

virtualBox虚拟机安装多个+主机访问虚拟机+虚拟机访问外网配置

目的&#xff1a;本机安装3个虚拟机 一、虚拟机安装&#xff1a;Oracle VM VirtualBox (https://www.virtualbox.org/)源代码可下载&#xff0c;且免费使用 1、https://www.virtualbox.org/ 进入网站中Download 模块选择与自己电脑系统相应的下载包下载即可 如果安装过程报错如…

京东数据分析:2023厨房小电市场遇冷,空气炸锅等明星产品被抛弃

过去几年间&#xff0c;宅经济的爆发带火了酸奶机、煮蛋器、豆浆机、空气炸锅、养生壶等&#xff0c;一众外观小巧、功能丰富、价格相对便宜的厨房小家电。但随着年轻人走出家门回归工作岗位&#xff0c;厨房小家电们却步入了艰难时刻。 如今&#xff0c;厨房小家电们似乎正在经…

论坛介绍 | COSCon'23 人工智能(A)

众多开源爱好者翘首期盼的开源盛会&#xff1a;第八届中国开源年会&#xff08;COSCon23&#xff09;将于 10月28-29日在四川成都市高新区菁蓉汇举办。本次大会的主题是&#xff1a;“开源&#xff1a;川流不息、山海相映”&#xff01;各位新老朋友们&#xff0c;欢迎到成都&a…

NET7下用WebSocket做简易聊天室

NET7下用WebSocket做简易聊天室 步骤&#xff1a; 建立NET7的MVC视图模型控制器项目创建websocket之间通信的JSON字符串对应的实体类一个房间用同一个Websocketwebsocket集合类&#xff0c;N个房间创建websocket中间件代码Program.cs中的核心代码&#xff0c;使用Websocket聊…