0

0

c++如何实现一个跳表(Skip List)_c++平衡树的高效替代方案

穿越時空

穿越時空

发布时间:2025-11-30 12:59:23

|

433人浏览过

|

来源于php中文网

原创

跳表是一种基于多层链表的动态数据结构,通过随机化层数实现平均O(log n)的查找、插入和删除性能;其核心由带前向指针数组的节点构成,利用头节点简化操作,从最高层开始逐层跳跃查找,插入时记录路径并更新各层指针,删除时断开连接并回收内存,同时可优化当前最大层数;相比平衡树,跳表代码更简洁、易于维护,且在并发场景下表现良好。

c++如何实现一个跳表(skip list)_c++平衡树的高效替代方案

跳表(Skip List)是一种基于概率的动态数据结构,能够以较低的实现复杂度达到与平衡树相近的性能。相比红黑树、AVL树等复杂的自平衡二叉搜索树,跳表在插入、删除和查找操作上平均时间复杂度均为 O(log n),且代码更简洁、易于理解和维护。它通过多层链表实现快速“跳跃”,从而提升查询效率。

跳表的基本原理

跳表本质上是一个多层有序链表。底层(第0层)包含所有元素,每一层都是下一层的“快速通道”。每个节点有一定概率向上提升一层(通常为50%),形成索引。查找时从最高层开始,像二分查找一样向右、向下移动,直到找到目标值。

主要特点:

  • 随机化层数:新节点的层数由随机函数决定,避免严格平衡带来的复杂调整。
  • 前向指针数组:每个节点维护一个指针数组,指向每一层的下一个节点。
  • 头节点简化操作:设置一个虚拟头节点,统一处理边界情况。

核心结构设计

定义跳表节点和主类结构:

立即学习C++免费学习笔记(深入)”;

// 跳表节点 struct SkipListNode { int val; std::vector forward; // 每一层的下一个节点指针 SkipListNode(int v, int level) : val(v), forward(level, nullptr) {} };

// 跳表主类 class SkipList { private: static const int MAX_LEVEL = 16; // 最大层数 SkipListNode* head; // 头节点 int currentLevel; // 当前最大有效层数

// 随机生成节点层数
int randomLevel() {
    int level = 1;
    while (rand() % 2 == 0 && level < MAX_LEVEL) {
        ++level;
    }
    return level;
}

public: SkipList() : currentLevel(1) { head = new SkipListNode(-1, MAX_LEVEL); }

查找操作实现

从最高层开始,向右直到下一个节点值大于目标,然后下降一层继续,最终在底层判断是否存在。

bool search(int target) { SkipListNode* curr = head; for (int i = currentLevel - 1; i >= 0; --i) { while (curr->forward[i] && curr->forward[i]->val forward[i]; } } curr = curr->forward[0]; return curr && curr->val == target; }

插入操作实现

先查找路径并记录每层最后访问的节点,再随机生成新节点层数,更新各层指针。

文心快码
文心快码

文心快码(Comate)是百度推出的一款AI辅助编程工具

下载
void add(int num) { std::vector update(MAX_LEVEL, head); SkipListNode* curr = head;
// 查找插入位置,记录每层最后一个小于num的节点
for (int i = currentLevel - 1; i >= 0; --i) {
    while (curr->forward[i] && curr->forward[i]->val < num) {
        curr = curr->forward[i];
    }
    update[i] = curr;
}

int newNodeLevel = randomLevel();
SkipListNode* newNode = new SkipListNode(num, newNodeLevel);

// 更新各层指针
for (int i = 0; i < newNodeLevel; ++i) {
    newNode->forward[i] = update[i]->forward[i];
    update[i]->forward[i] = newNode;
}

// 更新当前最大层数
if (newNodeLevel > currentLevel) {
    currentLevel = newNodeLevel;
}

}

删除操作实现

查找目标节点,若存在则逐层断开连接,并回收内存。同时检查是否需要降低当前最大层数。

bool erase(int num) { std::vector update(MAX_LEVEL, nullptr); SkipListNode* curr = head;
for (int i = currentLevel - 1; i >= 0; --i) {
    while (curr->forward[i] && curr->forward[i]->val < num) {
        curr = curr->forward[i];
    }
    update[i] = curr;
}

curr = curr->forward[0];
if (!curr || curr->val != num) return false;

// 断开各层指针
for (int i = 0; i < currentLevel; ++i) {
    if (update[i]->forward[i] != curr) break;
    update[i]->forward[i] = curr->forward[i];
}

delete curr;

// 降低当前层数(可选优化)
while (currentLevel > 1 && head->forward[currentLevel-1] == nullptr) {
    --currentLevel;
}

return true;

}

完整的析构函数可以遍历底层链表释放所有节点,防止内存泄漏。

跳表作为平衡树的替代方案,优势在于实现简单、并发友好(如ConcurrentSkipListMap)、支持范围查询高效。虽然最坏情况性能不如严格平衡树,但平均表现足够优秀,适合大多数场景。

基本上就这些。不复杂但容易忽略细节,比如随机层数控制和指针更新顺序。写好后多测几个边界用例即可稳定使用。

相关专题

更多
while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

90

2023.09.25

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

524

2023.09.20

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

524

2023.09.20

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

318

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

538

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

52

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

197

2025.08.29

javascriptvoid(o)怎么解决
javascriptvoid(o)怎么解决

javascriptvoid(o)的解决办法:1、检查语法错误;2、确保正确的执行环境;3、检查其他代码的冲突;4、使用事件委托;5、使用其他绑定方式;6、检查外部资源等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

175

2023.11.23

xml格式相关教程
xml格式相关教程

本专题整合了xml格式相关教程汇总,阅读专题下面的文章了解更多详细内容。

0

2026.01.19

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
HTML5/CSS3/JavaScript/ES6入门课程
HTML5/CSS3/JavaScript/ES6入门课程

共102课时 | 6.7万人学习

前端基础到实战(HTML5+CSS3+ES6+NPM)
前端基础到实战(HTML5+CSS3+ES6+NPM)

共162课时 | 18.9万人学习

第二十二期_前端开发
第二十二期_前端开发

共119课时 | 12.5万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号