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日

相关文章

  • 如何查看进程实际的内存占用情况详解

    要查看一个进程占用的实际内存占用情况,可以使用Linux系统的工具,下面介绍两种不同的方法进行操作。方法一使用top命令,方法二使用ps命令。 方法一:使用top命令 top命令可以显示当前系统的进程情况,其中也包含了进程的内存占用情况。以下是查看进程实际内存占用的步骤: 以root用户登录到服务器终端。 执行 top -p <PID> 命令,其…

    C 2023年5月23日
    00
  • 全境封锁2武器有哪些 全武器介绍

    全境封锁2武器有哪些 全武器介绍 全境封锁2是一款以军事背景为主题的 RPG 游戏,其中武器种类丰富。本文将对这些武器进行全面介绍。 武器种类 全境封锁2中的武器大致可分为以下几类: 步枪 冲锋枪 狙击枪 轻机枪 战斗霰弹枪 手枪 火焰喷射器 黄金枪 不同武器介绍 步枪 步枪是一类长枪,常见的有 AK47、M16A2 等。通常适用于中远距离作战,威力较大,但…

    C 2023年5月22日
    00
  • GCC 编译使用动态链接库和静态链接库的方法

    当我们编写C或C++代码时,我们经常需要使用堆、栈和内存分配等等功能,而这些功能代码通常不在我们自己的项目中。为了让这些代码能够在我们的代码中工作,我们需要链接库,这些库分为两种:动态链接库和静态链接库。本文将详细讲解GCC编译使用动态链接库和静态链接库的方法,并提供两条示例说明。 动态链接库 动态链接库(Dynamic Linking Library)是指…

    C 2023年5月23日
    00
  • 惠普新ENVY 13笔记本值得买吗 惠普新ENVY 13轻薄本深度图解评测

    惠普新ENVY 13笔记本深度评测攻略 简介 惠普新ENVY 13是一款定位于高端轻薄本的笔记本电脑。该产品采用了第11代英特尔酷睿处理器,具有出色的性能表现。这款笔记本还拥有高分辨率的13.3英寸触控屏幕、优秀的键盘、内置GPU、卓越的音效等特点。在设计方面,惠普新ENVY 13采用金属机身,轻薄便携,颜值也非常高。本文将深度讲解惠普新ENVY 13的各方…

    C 2023年5月22日
    00
  • 使命召唤14二战提示0xc000007b错误怎么办?

    当玩家在打开“使命召唤14二战”游戏时,遇到错误提示0xc000007b错误时,可能会感到困惑和沮丧。此错误提示意味着游戏无法启动,并且玩家将无法进入游戏。但是,这种错误通常可以通过以下步骤来修复: STEP 1:重新安装Microsoft Visual C++ Redistributable包 此错误的一个常见原因是缺失或损坏了Microsoft Visu…

    C 2023年5月23日
    00
  • Python练习之操作SQLite数据库

    下面是Python练习之操作SQLite数据库的完整攻略: 1. SQLite数据库简介 SQLite是一款轻型的关系型数据库,可以支持SQL语言标准的绝大部分功能,并且相对于其他的关系型数据库,SQLite更加便携、灵活和易于学习。Python作为一款著名的解释型编程语言,自带了SQLite数据库库,可以直接在Python中操作SQLite数据库。 2. …

    C 2023年5月23日
    00
  • windows下vscode使用cmake的方法

    下面是详细的讲解“Windows下VSCode使用CMake的方法”的完整攻略。 1. 安装环境 首先需要安装以下软件: Visual Studio Code CMake C/C++编译器 其中CMake和C/C++编译器可以使用MinGW-w64或者Visual Studio。 2. 创建CMake项目 在VSCode中打开一个空白的文件夹,然后使用以下命…

    C 2023年5月23日
    00
  • 详解C++的JSON静态链接库JsonCpp的使用方法

    下面是“详解C++的JSON静态链接库JsonCpp的使用方法”的完整攻略: 简介 JsonCpp是C++中实现JSON格式数据解析和生成的一种开源静态链接库。它可以解析、读取和生成JSON数据,使用简单方便,可移植性强,并且支持多种操作系统和编译器。 官网地址:https://github.com/open-source-parsers/jsoncpp 使…

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