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日

相关文章

  • ProE怎么设计立体的C型轮廓?

    要设计立体的C型轮廓,可以遵循以下步骤: 步骤一:绘制基本轮廓线 首先,打开ProE软件并创建一个新的零件。然后选择绘图工具中的“草图”工具,开始绘制轮廓线的基本形状。 例如,可以先绘制上部和底部线条,然后在中间画上一条垂直线条将两条线连接起来。在绘图时,需要注意轮廓曲线应该是封闭的,因为这是一个立体的轮廓线。在草图中可以加入尺寸,以确保轮廓大小和位置的准确…

    C 2023年5月23日
    00
  • python使用json序列化datetime类型实例解析

    以下是详细讲解“python使用json序列化datetime类型实例解析”的完整攻略: 什么是datetime类型 datetime是Python标准库中的一个模块,它提供了一系列处理日期和时间的函数。其中最主要的是datetime类,它定义了一种操作日期和时间的标准方法。 datetime与json相结合 在Python中,我们经常需要将数据序列化为JS…

    C 2023年5月23日
    00
  • CMake的简单应用

    请允许我来讲解“CMake的简单应用”的完整攻略。 什么是 CMake CMake 是一个跨平台的编译构建工具,它可以用来自动生成 Makefile、Visual Studio 的项目、XCode 的工程等等编译构建相关的文件。 它可以帮助我们更方便地管理和构建跨平台的项目,提高开发效率和代码可维护性。下面我们将介绍如何使用 CMake 来构建项目。 CMa…

    C 2023年5月23日
    00
  • C语言对于volatile与gcc优化的探究

    C语言对于volatile与gcc优化的探究 什么是volatile关键字 在C语言中,volatile是一个关键字,可以用来修饰一个变量,告诉编译器这个变量没有被优化,需要实时读取。 volatile的作用是防止编译器进行一些优化,例如在一个循环中,变量的值在循环中被修改,而且这个变量还被其他模块所使用,那么为了保证其他模块使用的变量是最新的,我们就需要用…

    C 2023年5月23日
    00
  • C#连接MySQL数据库的方法步骤

    下面是C#连接MySQL数据库的方法步骤的完整攻略。 1. 准备工作 在连接MySQL数据库之前,需要在计算机上安装MySQL数据库,并创建相应的数据库和数据表。此外,还需要下载MySql.Data.dll和MySQL Connector/NET。在连接MySQL数据库之前,还需要在Visual Studio中引用这些dll。 2. 导入命名空间 在C#代码…

    C 2023年5月22日
    00
  • C语言volatile关键字的作用与示例

    C语言中的volatile关键字可以用于修饰被多线程访问或外部环境影响的变量,以保证程序访问这些变量的正确性。本文将从定义、作用、使用方法以及实例方面全面介绍volatile关键字的使用。 定义 volatile是C语言的关键字,表示“易变的、多变的、易波动的”,即表示一个全局变量或局部变量,其值可能随时会发生改变,因此每次访问该变量时都必须重新读取变量的值…

    C 2023年5月23日
    00
  • 基于一致性hash算法 C++语言的实现详解

    下面是 “基于一致性Hash算法C++语言的实现详解” 的攻略。 简介 一致性Hash算法是分布式系统中常用的一种负载均衡算法。C++ 语言是一种高效的编程语言,有着广泛的应用。本篇攻略将通过分析一致性Hash算法的实现,详细讲解如何在C++语言中实现这一算法,并给出两个示例说明。 一致性Hash算法的实现 步骤一:将服务器节点映射到一个环上 一致性Hash…

    C 2023年5月22日
    00
  • C++初识类和对象

    C++初识类和对象 什么是类和对象? 在C++中,类和对象是两个重要概念,类是一种用户自定义的数据类型,它是一组数据和操作数据的函数的集合,而对象是类的一个实例,是具体的、有形的存在。可以通过对象来使用类中的函数和数据。 如何定义一个类? 定义一个类,需要使用关键字class,语法如下: class 类名 { public: // 公共成员函数和成员变量 p…

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