0

0

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

冰火之心

冰火之心

发布时间:2026-01-14 17:00:31

|

123人浏览过

|

来源于php中文网

原创

跳表通过随机化层级实现平衡,插入时动态生成层数并更新每层指针,删除需基于查找路径逐层断开且维护currentLevel,多线程需加锁,测试需固定随机种子。

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

跳表的核心设计:层级结构与随机化晋升

跳表不是靠旋转或重构维持平衡,而是用概率控制节点在多层链表中的出现频率。关键在于:越高层的链表节点越少,但跨度越大。插入时用随机数决定新节点出现在几层——通常用 rand() % 2 == 0 模拟 50% 晋升概率,这样期望层数是 O(log n),查询均摊也是 O(log n)

不要用固定层数(如强行 16 层),而应在每次插入时动态生成层级:

int randomLevel() {
    int level = 1;
    while (rand() % 2 == 0 && level < MAX_LEVEL) {
        level++;
    }
    return level;
}
注意 MAX_LEVEL 建议设为 32 左右(log₂(1e9) 量级足够),太大浪费内存,太小会退化。

节点定义与内存布局影响性能

跳表性能瓶颈常不在算法逻辑,而在缓存友好性。每个节点要存 vector forward 或固定长度数组。推荐用 Node** forward + 内存连续分配(即把所有层级指针紧挨着 malloc 出来),避免 vector 动态扩容和指针跳转开销。

struct Node {
    int key;
    int value;
    Node** forward; // 指向各层后继的指针数组
    Node(int k, int v, int level) : key(k), value(v) {
        forward = new Node*[level]{}; // 初始化为 nullptr
    }
    ~Node() { delete[] forward; }
};
务必在析构中释放 forward,否则严重泄漏。另外,keyvalue 类型建议模板化,但初版先用 int 验证逻辑更稳妥。

查找与插入必须复用「搜索路径」

跳表高效的关键是:一次向下遍历就能同时记录每层的插入位置。不要写两个独立函数——查找返回 nullptr,插入再搜一遍。应该统一用 vector update 记录每层最后一个小于目标 key 的节点。

vector findPath(int key) {
    vector update(MAX_LEVEL, head);
    Node* x = head;
    for (int i = currentLevel - 1; i >= 0; i--) {
        while (x->forward[i] && x->forward[i]->key < key) {
            x = x->forward[i];
        }
        update[i] = x;
    }
    return update;
}
插入时直接调用它,然后对每一层新建节点并更新指针。漏掉某一层的 update[i] 更新,就会导致链表断裂或跳过节点。

删除操作容易忽略并发与重复 key 场景

标准跳表通常假设 key 唯一。若允许多值,需额外处理(比如在节点内用 list 存 value)。删除时仍要先调用 findPath,再逐层断开链接——但注意:只断开实际存在的层级,不能硬写 for (int i = 0; i ,因为节点可能只有 3 层,而 currentLevel 是运行时最大层数。

UP简历
UP简历

基于AI技术的免费在线简历制作工具

下载

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

常见错误:

  • 没检查 x->forward[i] 是否非空就赋值
  • 删除后没更新 currentLevel(当最高层只剩 head 时应下移)
  • 多线程下未加锁,尤其 headcurrentLevel 是全局共享状态
单线程可省锁,但生产环境必须考虑 std::shared_mutex 或 RCU 方案。

跳表真正难的不是代码长度,而是层级随机性带来的测试不确定性——同样的输入序列,每次运行层级分布不同。调试时建议临时固定随机种子,或打印每步的 update 数组内容。另外,currentLevel 的维护稍有不慎,整个上层结构就失效,这点比红黑树的旋转规则更隐蔽。

相关文章

c++速学教程(入门到精通)
c++速学教程(入门到精通)

c++怎么学习?c++怎么入门?c++在哪学?c++怎么学才快?不用担心,这里为大家提供了c++速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
string转int
string转int

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

315

2023.08.02

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

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

537

2024.08.29

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

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

52

2025.08.29

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

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

194

2025.08.29

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

480

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

143

2025.12.24

页面置换算法
页面置换算法

页面置换算法是操作系统中用来决定在内存中哪些页面应该被换出以便为新的页面提供空间的算法。本专题为大家提供页面置换算法的相关文章,大家可以免费体验。

400

2023.08.14

Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

0

2026.01.14

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

12

2026.01.13

热门下载

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

精品课程

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

共102课时 | 6.7万人学习

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

共162课时 | 18.8万人学习

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

共119课时 | 12.4万人学习

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

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