c++如何实现跳表(skiplist)

跳表(skiplist)是一种随机化的数据结构,它允许快速查询一个有序序列中的元素,并且它的插入和删除操作具有相对较低的时间复杂度。下面我们将介绍如何使用C++实现跳表。

基本思路

跳表的基本思路是建立多层索引,即使用多级指针来跳过一些元素,在链表的基础上进行优化。第一层是原始链表,其他层则是链表的子集。每一层的元素数量越来越少,随着层数的增加,跳过元素的能力也增加,查询、插入、删除等操作的时间复杂度也得到了优化。

实现步骤

  1. 定义跳表结构体
struct SkipNode {
    int key;            
    int value;          
    vector<SkipNode*> forward;  // 前向指针数组
    SkipNode(int k, int v, int level): key(k), value(v) {
        forward.resize(level, nullptr);
    }
};
  1. 定义跳表类
class SkipList {
public:
    SkipList(double p, int maxLevel);
    ~SkipList();   
    bool find(int key, int& value);
    void insert(int key, int value);
    bool remove(int key);
    void print();
private:
    int randomLevel();
    SkipNode* createNode(int key, int value, int level);
    void deleteNode(SkipNode* node);
private:
    double probability_;   // 控制每一层索引出现的概率
    int maxLevel_;         // 跳表的最大层数
    SkipNode* head_;       // 头节点
};
  1. 实现跳表构造函数
SkipList::SkipList(double p, int maxLevel): probability_(p), maxLevel_(maxLevel) {
    head_ = createNode(-1, -1, maxLevel_);  // 初始化头节点
}
  1. 实现随机层数函数
int SkipList::randomLevel() {
    int level = 1;
    while (rand() % 2 == 0 && level < maxLevel_) {
        level++;
    }
    return level;
}
  1. 实现新节点创建函数
SkipNode* SkipList::createNode(int key, int value, int level) {
    return new SkipNode(key, value, level);
}
  1. 实现节点删除函数
void SkipList::deleteNode(SkipNode* node) {
    if (node != nullptr) {
        for (int i = 0; i < node->forward.size(); i++) {
            delete node->forward[i];
        }
        delete node;
    }
}
  1. 实现插入操作
void SkipList::insert(int key, int value) {
    SkipNode* cur = head_;
    vector<SkipNode*> update(maxLevel_, nullptr);
    for (int i = maxLevel_ - 1; i >= 0; i--) {
        while (cur->forward[i] != nullptr && cur->forward[i]->key < key) {
            cur = cur->forward[i];
        }
        update[i] = cur;
    }
    cur = cur->forward[0];
    if (cur == nullptr || cur->key != key) {
        int level = randomLevel();
        SkipNode* node = createNode(key, value, level);
        for (int i = 0; i < level; i++) {
            node->forward[i] = update[i]->forward[i];
            update[i]->forward[i] = node;
        }
    }
}
  1. 实现查找操作
bool SkipList::find(int key, int& value) {
    SkipNode* cur = head_;
    for (int i = maxLevel_ - 1; i >= 0; i--) {
        while (cur->forward[i] != nullptr && cur->forward[i]->key < key) {
            cur = cur->forward[i];
        }
    }
    cur = cur->forward[0];
    if (cur != nullptr && cur->key == key) {
        value = cur->value;
        return true;
    }
    return false;
}
  1. 实现删除操作
bool SkipList::remove(int key) {
    SkipNode* cur = head_;
    vector<SkipNode*> update(maxLevel_, nullptr);
    for (int i = maxLevel_ - 1; i >= 0; i--) {
        while (cur->forward[i] != nullptr && cur->forward[i]->key < key) {
            cur = cur->forward[i];
        }
        update[i] = cur;
    }
    cur = cur->forward[0];
    if (cur != nullptr && cur->key == key) {
        for (int i = 0; i < cur->forward.size(); i++) {
            update[i]->forward[i] = cur->forward[i];
        }
        deleteNode(cur);
        return true;
    }
    return false;
}
  1. 实现打印操作
void SkipList::print() {
    for (int i = maxLevel_ - 1; i >= 0; i--) {
        SkipNode* cur = head_;
        cout << "Level " << i << ": ";
        while (cur->forward[i] != nullptr) {
            cout << "(" << cur->forward[i]->key << "," << cur->forward[i]->value << ") ";
            cur = cur->forward[i];
        }
        cout << endl;
    }
}

示例说明

示例一:插入操作

SkipList sl(0.5, 5);
for (int i = 0; i < 20; i++) {
    sl.insert(i, i * i);
}
sl.print();

输出结果:

Level 4: (16,256) 
Level 3: (2,4) (4,16) (6,36) (7,49) (8,64) (9,81) (11,121) (12,144) (13,169) (15,225) (16,256) (17,289) (18,324) 
Level 2: (2,4) (4,16) (6,36) (7,49) (8,64) (9,81) (11,121) (12,144) (13,169) (15,225) (16,256) (17,289) (18,324) 
Level 1: (2,4) (4,16) (6,36) (7,49) (8,64) (9,81) (11,121) (12,144) (13,169) (15,225) (16,256) (17,289) (18,324) 
Level 0: (0,0) (2,4) (4,16) (6,36) (7,49) (8,64) (9,81) (10,100) (11,121) (12,144) (13,169) (14,196) (15,225) (16,256) (17,289) (18,324) (19,361)

其中,我们先创建了一个最大层数为5,索引出现概率为0.5的跳表对象sl,然后插入20个元素,最后调用print函数打印跳表的状态。可以看到,print函数打印出了跳表中每一层的所有元素。

示例二:删除操作

SkipList sl(0.5, 5);
for (int i = 0; i < 20; i++) {
    sl.insert(i, i * i);
}
sl.remove(8);
sl.remove(6);
sl.remove(12);
sl.print();

输出结果:

Level 4: (16,256) 
Level 3: (2,4) (4,16) (7,49) (9,81) (11,121) (13,169) (15,225) (16,256) (17,289) (18,324) 
Level 2: (2,4) (4,16) (7,49) (9,81) (11,121) (13,169) (15,225) (16,256) (17,289) (18,324) 
Level 1: (2,4) (4,16) (7,49) (9,81) (11,121) (13,169) (15,225) (17,289) (18,324) 
Level 0: (0,0) (2,4) (4,16) (7,49) (9,81) (11,121) (13,169) (15,225) (17,289) (18,324) (19,361)

其中,我们先创建了一个最大层数为5,索引出现概率为0.5的跳表对象sl,然后插入20个元素,最后依次删除了键为8、6和12的元素,最后调用print函数打印跳表的状态。可以看到,print函数打印出了跳表中每一层的所有元素,删除的元素也不再存在其中。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:c++如何实现跳表(skiplist) - Python技术站

(0)
上一篇 2023年5月23日
下一篇 2023年5月23日

相关文章

  • C程序 寻找两个整数之间的阿姆斯特朗数字

    C程序 寻找两个整数之间的阿姆斯特朗数字使用攻略 概述 该程序是一个 C 语言的代码,用于寻找两个整数之间的阿姆斯特朗数字。阿姆斯特朗数字指的是一个 n 位数 (n ≥ 3),它的每个数位上的数字的 n 次幂之和恰好等于它本身。例如,1³ + 5³ + 3³ = 153。 程序运行环境 操作系统:Windows或Linux 编程语言:C语言 编译器:GCC编…

    C 2023年5月9日
    00
  • Qt利用QJson实现解析数组的示例详解

    以下是“Qt利用QJson实现解析数组的示例详解”的完整攻略: 1. 引入QJson库 在Qt项目中使用QJson,需要在.pro文件中添加以下代码引入QJson库: QT += network LIBS += -lqjson 2. 解析JSON字符串 使用QJson库进行解析,首先需要将JSON字符串转成QJsonDocument类型,然后调用QJsonD…

    C 2023年5月23日
    00
  • C# JSON格式化转换辅助类 ConvertJson

    C#是一种广泛使用的面向对象编程语言,而JSON格式化转换是现代程序中广泛使用的数据交换方式,将一个对象或一组对象序列化为JSON格式数据非常常见。ConvertJson是一个C# JSON格式化转换辅助类,在处理JSON格式数据时非常实用。接下来,我将为您提供关于如何使用ConvertJson的完整攻略。 安装 ConvertJson可以从NuGet包中获…

    C 2023年5月23日
    00
  • 计算一个Java对象占用字节数的方法

    计算一个Java对象占用字节数需要分别考虑对象头和实例数据的大小。接下来将介绍Java对象头和实例数据的大小,并提供两条示例说明。 Java对象头的大小 Java对象头的大小并不是固定的,由不同虚拟机实现决定,一般包含以下几个部分: 对象的哈希码和GC分代年龄:占用4个字节。 锁信息:占用4个字节。 类型指针:占用4个字节或8个字节,取决于指针压缩。如果开启…

    C 2023年5月22日
    00
  • 一文详解Qt中的对象树机制

    一文详解Qt中的对象树机制 什么是对象树机制? 在 Qt 中,每一个对象都有其父对象,这些对象之间形成了一种树形结构,我们称之为 对象树。当一个对象被创建时,可以设置它的父对象,然后它就会成为父对象的子对象,加入到对象树中。 Qt 中的对象树机制,可以实现对象之间的自动管理,并沿着树形结构进行自动的构建、销毁和内存管理。 对象树的作用 对象树机制的主要作用:…

    C 2023年5月22日
    00
  • C语言的动态内存管理的深入了解

    C语言的动态内存管理的深入了解 什么是动态内存 在 C 语言中,动态内存是由程序员在运行时分配的内存。与之相对的是静态内存,即在编译器静态分配的内存。动态内存分配在需要的时候进行,这使得程序在运行时更加灵活。 在 C 语言中,动态内存的分配和管理不同于栈空间和全局/静态内存。程序员可以使用几个库函数来进行动态内存分配和释放,这个过程也称为 动态内存管理 。 …

    C 2023年5月22日
    00
  • C++简单集合类的实现方法

    C++简单集合类的实现方法 什么是集合类? 集合类是数据结构中的一种,用来存储一组相同类型的数据项。集合类可以快速的对其中的数据进行添加、删除、查找、排序等操作。在C++中,STL中的集合类就是其中之一。 集合类实现原理 在实现一个集合类时,我们可以使用数组、链表、哈希表等数据结构。不过,在这里我们使用了一个常用的数据结构:红黑树。 红黑树是一种自平衡二叉搜…

    C 2023年5月23日
    00
  • Win10怎么设置MTU值加快WIFI速度?

    针对“Win10怎么设置MTU值加快WIFI速度?”这个问题,下面是我提供的完整攻略: 1. 了解MTU值 MTU(Maximum Transmission Unit)即最大传输单元,是每个数据包可以传输的最大数据量。通常情况下,MTU值越大,一个数据包就可以携带更多的数据,从而提高网络传输效率。但如果MTU值设置得过大,会增加传输过程中出现网络问题的风险。…

    C 2023年5月22日
    00
合作推广
合作推广
分享本页
返回顶部