【数据结构】跳表SkipList代码解析(C++)

简介: 【数据结构】跳表SkipList代码解析(C++)

跳表SkipList解析

什么是跳表

这里不做介绍,详见:

代码解析

主要理解点

  • 先来张图

层次感

  1. 各个节点是如何相连接(关联)的?

    • 通过每个节点的forward数组,forward数组存储当前节点,在每一层的下一个节点。
    • 以头节点为例,头结点的forward存储的是每一层的第一个节点。然后通过第一个节点的forward[level],拿到该层的后面元素...... 以次类推,即可遍历该层所有节点。
    • 与普通单链表的区别,我们可以大概理解为,上面多了几层简化的结果,如上面动画所示。
  2. update存的是什么?

    • 每层中最后一个key小于要插入节点key的节点。
  3. 节点生成的level是多少,该节点就从0层到level层一直都出现。
  4. 其它详见具体代码中的注释

相关类&成员函数

节点类Node

template<typename K, typename V> 
class Node {
public:
    // 无参构造
    Node() {} 
    // 有参构造-最后一个参数为层数
    Node(K k, V v, int); 
    // 析构
    ~Node();
    // 拿到当前节点key值
    K get_key() const;
    // 拿到当前节点value值
    V get_value() const;
    // 设置value值
    void set_value(V); 
    // forward-存储当前结点在i层的下一个结点。
    Node<K, V> **forward;
    //所在层级
    int node_level;
private:
    K key;
    V value;
};

跳表类SkipList

template <typename K, typename V> 
class SkipList {

public: 
    // 有参构造-参数为最大层数
    SkipList(int);
    // 析构
    ~SkipList();
    // 生成随机层数
    int get_random_level();
    // 创建节点-最后一个参数为所在层数
    Node<K, V>* create_node(K, V, int);
    // 插入数据
    int insert_element(K, V);
    // 打印数据-每层都打印
    void display_list();
    // 查找元素
    bool search_element(K);
    // 删除元素
    void delete_element(K);
    // 存入文件
    void dump_file();
    // 加载文件
    void load_file();
    // 返回节点个数
    int size();

private:
    // 从文件中的一行读取key和value
    void get_key_value_from_string(const std::string& str, std::string* key, std::string* value);
    // 是否为有效的string
    bool is_valid_string(const std::string& str);

private:    
    // 该跳表最大层数
    int _max_level;

    // 该跳表当前层数
    int _skip_list_level;

    // 跳表头节点
    Node<K, V> *_header;

    // 文件操作
    std::ofstream _file_writer;
    std::ifstream _file_reader;

    // 该跳表当前的元素数
    int _element_count;
};

详解insert函数

节点的插入遍历拿到update数组

Node<K, V> *update[_max_level+1];//使用_max_level+1开辟,使空间,肯定够,因为创建节点的时候,会对随机生成的key进行限制。
memset(update, 0, sizeof(Node<K, V>*)*(_max_level+1));  

// 从跳表左上角开始查找——_skip_list_level为当前所存在的最高的层(一共有多少层则需要+1,因为是从level=0层开始的)
for(int i = _skip_list_level; i >= 0; i--) {//控制当前所在层,从最高层到第0层

    //从每一层的最左边开始遍历,如果该节点存在并且,key小于我们要插入的key,继续在该层后移。
    while(current->forward[i] != NULL && current->forward[i]->get_key() < key) {//是不是继续往后面走
        current = current->forward[i]; //提示: forward存储该节点在当前层的下一个节点
    }
    update[i] = current;//保存
    //切换下一层
}

//将current指向第0层第一个key大于要插入节点key的节点。
//下面用current用来判断要插入的节点存key是否存在
//即,如果该key不存在的话,准备插入到update[i]后面。存在,则修改该key对应的value。
current = current->forward[0];

在这里插入图片描述


更新层次

// 为当前要插入的节点生成一个随机层数
int random_level = get_random_level();

//如果随机出来的层数大于当前链表达到的层数,注意:不是最大层,而是当前的最高层_skip_list_level。
//更新层数,更新update,准备在每层([0,random_level]层)插入新元素。
if (random_level > _skip_list_level) {
    for (int i = _skip_list_level+1; i < random_level+1; i++) {
        update[i] = _header;
    }
    _skip_list_level = random_level;
}

更新层次


插入节点

 // 创建节点
Node<K, V>* inserted_node = create_node(key, value, random_level);

// 插入节点
for (int i = 0; i <= random_level; i++) {
    //在每一层([0,random_level])
    //先将原来的update[i]的forward[i]放入新节点的forward[i]。
    //再将新节点放入update[i]的forward[i]。
    inserted_node->forward[i] = update[i]->forward[i];//新节点与后面相链接
    update[i]->forward[i] = inserted_node;//新节点与前面相链接
}
//std::cout << "Successfully inserted key:" << key << ", value:" << value << std::endl;
_element_count++;//元素总数++

插入节点


相关参考


相关文章
|
12天前
|
C++ Windows
应用程序无法正常启动(0xc0000005)?C++报错0xC0000005如何解决?使命召唤17频频出现闪退,错误代码0xC0000005(0x0)
简介: 本文介绍了Windows应用程序出现错误代码0xc0000005的解决方法,该错误多由C++运行库配置不一致或内存访问越界引起。提供包括统一运行库配置、调试排查及安装Visual C++运行库等解决方案,并附有修复工具下载链接。
499 1
|
5月前
|
前端开发 Java
java实现队列数据结构代码详解
本文详细解析了Java中队列数据结构的实现,包括队列的基本概念、应用场景及代码实现。队列是一种遵循“先进先出”原则的线性结构,支持在队尾插入和队头删除操作。文章介绍了顺序队列与链式队列,并重点分析了循环队列的实现方式以解决溢出问题。通过具体代码示例(如`enqueue`入队和`dequeue`出队),展示了队列的操作逻辑,帮助读者深入理解其工作机制。
164 1
|
6月前
|
算法 PyTorch 算法框架/工具
昇腾 msmodelslim w8a8量化代码解析
msmodelslim w8a8量化算法原理和代码解析
457 5
|
2月前
|
存储 监控 算法
基于跳表数据结构的企业局域网监控异常连接实时检测 C++ 算法研究
跳表(Skip List)是一种基于概率的数据结构,适用于企业局域网监控中海量连接记录的高效处理。其通过多层索引机制实现快速查找、插入和删除操作,时间复杂度为 $O(\log n)$,优于链表和平衡树。跳表在异常连接识别、黑名单管理和历史记录溯源等场景中表现出色,具备实现简单、支持范围查询等优势,是企业网络监控中动态数据管理的理想选择。
82 0
|
7月前
|
存储 安全 C语言
C++ String揭秘:写高效代码的关键
在C++编程中,字符串操作是不可避免的一部分。从简单的字符串拼接到复杂的文本处理,C++的string类为开发者提供了一种更高效、灵活且安全的方式来管理和操作字符串。本文将从基础操作入手,逐步揭开C++ string类的奥秘,帮助你深入理解其内部机制,并学会如何在实际开发中充分发挥其性能和优势。
|
2月前
|
API 数据安全/隐私保护 C++
永久修改机器码工具, exe一机一码破解工具,软件机器码一键修改工具【c++代码】
程序实现了完整的机器码修改功能,包含进程查找、内存扫描、模式匹配和修改操作。代码使用
|
3月前
|
C++
爱心代码 C++
这段C++代码使用EasyX图形库生成动态爱心图案。程序通过数学公式绘制爱心形状,并以帧动画形式呈现渐变效果。运行时需安装EasyX库,教程链接:http://【EasyX图形库的安装和使用】https://www.bilibili.com/video/BV1Xv4y1p7z1。代码中定义了屏幕尺寸、颜色数组等参数,利用随机数与数学函数生成动态点位,模拟爱心扩散与收缩动画,最终实现流畅的视觉效果。
290 0
|
7月前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》图、查找、排序专题考点(含解析)
408考研——《数据结构》图,查找和排序专题考点选择题汇总(含解析)。
288 29
|
6月前
|
传感器 监控 Java
Java代码结构解析:类、方法、主函数(1分钟解剖室)
### Java代码结构简介 掌握Java代码结构如同拥有程序世界的建筑蓝图,类、方法和主函数构成“黄金三角”。类是独立的容器,承载成员变量和方法;方法实现特定功能,参数控制输入环境;主函数是程序入口。常见错误包括类名与文件名不匹配、忘记static修饰符和花括号未闭合。通过实战案例学习电商系统、游戏角色控制和物联网设备监控,理解类的作用、方法类型和主函数任务,避免典型错误,逐步提升编程能力。 **脑图速记法**:类如太空站,方法即舱段;main是发射台,static不能换;文件名对仗,括号要成双;参数是坐标,void不返航。
251 5
|
7月前
|
存储 机器学习/深度学习 人工智能
C 408—《数据结构》易错考点200题(含解析)
408考研——《数据结构》精选易错考点200题(含解析)。
506 27

热门文章

最新文章

推荐镜像

更多
  • DNS