c++实现跳表
原理
跳表(Skip List) 是一种随机化数据结构,用于高效查找、插入和删除,尤其适用于有序数据集合。相比链表,跳表通过多层索引结构加速查找,期望时间复杂度接近 O(logn)。跳表的主要思想是:
- 底层链表存储所有数据元素,保持有序。
- 上层链表是稀疏索引,用于跳过部分节点,减少遍历的长度。
结构图
下面是一个跳表的结构示意:
Level 3: [1] ----------------> [9]
Level 2: [1] -----> [4] -----> [9]
Level 1: [1] -----> [4] -----> [7] -----> [9]
Level 0: [1] -> [2] -> [4] -> [5] -> [7] -> [8] -> [9]
如上图所示:
- 每一层都是一个有序链表。
- 每一层的节点是下一层的子集,存储重要的中间节点,形成分层索引。
- 查找过程:从最高层开始,先向右移动,如果目标值超出范围,则向下移动到下一层。重复此过程直到找到目标节点。
优缺点
优点:
- 插入、删除和查找的期望时间复杂度为 O(logn)。
- 实现简单,比红黑树和AVL树更容易理解和维护。
缺点:
- 需要额外的空间存储索引层节点。
代码示例(c++)
下面是一段简单的 C++ 跳表实现,包括节点结构定义、插入、查找和显示功能。
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>
using namespace std;class Node {
public:int value;vector<Node*> forward; // 每层的前向指针Node(int val, int level) : value(val), forward(level + 1, nullptr) {}
};class SkipList {
private:int maxLevel; // 跳表的最大层数float probability; // 晋升概率Node* header; // 头节点int currentLevel; // 当前跳表的层数public:SkipList(int maxLevel, float probability) : maxLevel(maxLevel), probability(probability), currentLevel(0) {header = new Node(-1, maxLevel); // 头节点初始化为-1srand(time(nullptr)); // 初始化随机数种子}// 随机生成节点的层数int randomLevel() {int lvl = 0;while ((rand() / double(RAND_MAX)) < probability && lvl < maxLevel) {lvl++;}return lvl;}// 插入新节点void insert(int value) {vector<Node*> update(maxLevel + 1);Node* current = header;// 从最高层向下查找插入位置for (int i = currentLevel; i >= 0; i--) {while (current->forward[i] && current->forward[i]->value < value) {current = current->forward[i];}update[i] = current;}// 在底层插入节点的位置current = current->forward[0];// 如果节点不存在,则插入新节点if (!current || current->value != value) {int lvl = randomLevel();if (lvl > currentLevel) {for (int i = currentLevel + 1; i <= lvl; i++) {update[i] = header;}currentLevel = lvl;}Node* newNode = new Node(value, lvl);for (int i = 0; i <= lvl; i++) {newNode->forward[i] = update[i]->forward[i];update[i]->forward[i] = newNode;}cout << "Inserted value: " << value << " at level: " << lvl << endl;}}// 查找节点bool search(int value) {Node* current = header;for (int i = currentLevel; i >= 0; i--) {while (current->forward[i] && current->forward[i]->value < value) {current = current->forward[i];}}current = current->forward[0];return current && current->value == value;}// 打印跳表结构void display() {for (int i = currentLevel; i >= 0; i--) {Node* current = header->forward[i];cout << "Level " << i << ": ";while (current) {cout << current->value << " ";current = current->forward[i];}cout << endl;}}
};int main() {SkipList skipList(4, 0.5); // 最大层数为4,晋升概率为0.5skipList.insert(3);skipList.insert(6);skipList.insert(7);skipList.insert(9);skipList.insert(12);skipList.insert(19);cout << "Skip List Structure:" << endl;skipList.display();cout << "Search 7: " << (skipList.search(7) ? "Found" : "Not Found") << endl;cout << "Search 4: " << (skipList.search(4) ? "Found" : "Not Found") << endl;return 0;
}
代码解析
- Node 类:
- 表示跳表中的节点,包含节点的值和指向不同层节点的指针向量
forward。
- 表示跳表中的节点,包含节点的值和指向不同层节点的指针向量
- SkipList 类:
- 实现了跳表的主要功能,包括插入、查找和显示结构。
randomLevel:用于随机生成节点的层数,控制索引的稀疏程度。insert:插入元素到跳表中,如果新节点的层数超过当前最大层数,则更新索引。search:查找目标值是否存在。
- 主函数:
- 初始化跳表并插入若干元素,测试插入和查找功能。
运行结果
Inserted value: 3 at level: 0
Inserted value: 6 at level: 1
Inserted value: 7 at level: 2
Inserted value: 9 at level: 0
Inserted value: 12 at level: 1
Inserted value: 19 at level: 3
Skip List Structure:
Level 3: 19
Level 2: 7 19
Level 1: 6 12 19
Level 0: 3 6 7 9 12 19
Search 7: Found
Search 4: Not Found
总结
- 时间复杂度:查找、插入、删除的期望时间复杂度为 O(logn)O(\log n)O(logn)。
- 空间复杂度:O(nlogn)O(n \log n)O(nlogn),因为每个节点可能会出现在多层中。
跳表的实现简单且高效,常用于 Redis 等数据库的有序集合。
相关文章:
c++实现跳表
原理 跳表(Skip List) 是一种随机化数据结构,用于高效查找、插入和删除,尤其适用于有序数据集合。相比链表,跳表通过多层索引结构加速查找,期望时间复杂度接近 O(logn)。跳表的主要思想是: …...
新探索研究生英语读写教程pdf答案(基础级)
《新探索研究生英语读写教程》的设计和编写充分考虑国内研究生人才培养目标和研究生公共英语的教学需求, 教学内容符合研究生认知水平, 学术特征突出;教学设计紧密围绕学术阅读、学术写作和学术研究能力培养;教学资源立体多元&…...
管道与共享内存
一,命名管道 管道的限制就是他只能在有血缘关系(父子进程)的进程中,允许互相访问,这是有局限性的,所以我们想在毫无关系的进程中允许他们相互访问,这就是命名管道的定义。 总结:命名…...
ES 自定义排序方式
es默认score是根据query的相关度进行打分的,具体打分机制可以参见:官方文档。如果召回时既希望有相关性又能根据其他信息进行排序。 例如小红书搜索的时候,可能既希望有召回相关度又能根据热度信息(如果喜欢、收藏等等参数去进行召…...
在vue中,编写一个li标签同时使用v-for和v-if,谁的优先级更高
在 Vue 中,v-if 和 v-for 是两个常用的指令,但它们的优先级不同。当二者一起使用时,v-for 的优先级高于 v-if。这意味着,v-for 会先执行,即使列表中的某些元素不满足 v-if 条件,它们仍会被遍历和渲染。 由…...
Java 后端开发面试题及其答案
以下是一些常见的 Java 后端开发面试题及其答案,涵盖了 Java 基础、面向对象、并发、多线程、框架等多个方面: 1. Java 中的基本数据类型有哪些? 答案: Java 中的基本数据类型有 8 种: int:32 位整数lon…...
C++,STL 045(24.10.24)
内容 1.对set容器的大小进行操作。 2.set容器的交换操作。 运行代码 #include <iostream> #include <set>using namespace std;void printSet(set<int> &s) {for (set<int>::iterator it s.begin(); it ! s.end(); it){cout << *it <…...
二叉树习题其五【力扣】【算法学习day.12】
前言 书接上篇文章二叉树习题其四,这篇文章我们将基础拓展 ###我做这类文档一个重要的目的还是给正在学习的大家提供方向(例如想要掌握基础用法,该刷哪些题?)我的解析也不会做的非常详细,只会提供思路和一…...
【数据库】Mysql的锁类型
Mysql中的锁机制主要是为了保证数据的一致性和完整性,在并发的情况下起着至关重要的作用。其中锁的类型主要是分为以下几种: 按照粒度分类 全局锁:对于整个数据库实例进行枷锁,加锁后整个实例就处于只读的状态。局锁通常用于需要…...
自媒体短视频制作素材下载网站推荐,让创作更简单
随着自媒体行业的火爆,视频质量要求也越来越高。想要找到无版权的高清视频素材并不容易,但别担心!今天为大家整理了5个国内外高质量的素材网站,让你轻松获取自媒体短视频素材,快收藏起来吧! 蛙学网 蛙学网是…...
Altium Designer 入门基础教程(五)
本文章继续接着《Altium Designer 入门基础教程(四)》的内容往下介绍: 七、AD画板的整个流程步骤 I.集成库的制作 AD元件库有2种:1、原理图元件库SCH.LIB 2、印刷电路板(PCB)元件库 PCB.LIB 印刷电路…...
Java题集练习3
Java题集练习3 1 什么时候用instanceof instanceOf关键字主要用于判断一个对象是否为某个类的子类或是接口的实例,通常用于类型转换和运行时类型判断的场景,比如继承和多态中。比如,创建一个Animal类及其子类Cat和Cat子类Hat,可…...
【部署篇】Haproxy-01安装部署(源码方式安装)
一、HAProxy概述 HAProxy是一款免费、快速且可靠的代理软件,提供高可用性、负载均衡,支持TCP和HTTP应用代理,HAProxy凭借其卓越的性能和灵活性,成为众多知名网站和系统的首选代理软件。 核心特点: 高性能…...
开拓鸿蒙测试新境界,龙测科技引领自动化测试未来
在当今科技舞台上,鸿蒙 OS 以非凡先进性强势登场,打破传统操作系统格局,为软件测试领域带来全新机遇与艰巨挑战。 一、鸿蒙 OS 的辉煌崛起 (一)壮丽发展历程与卓越市场地位 鸿蒙 OS 的发展如波澜壮阔的史诗。2023 年…...
Java项目-基于springboot框架的自习室预订系统项目实战(附源码+文档)
作者:计算机学长阿伟 开发技术:SpringBoot、SSM、Vue、MySQL、ElementUI等,“文末源码”。 开发运行环境 开发语言:Java数据库:MySQL技术:SpringBoot、Vue、Mybaits Plus、ELementUI工具:IDEA/…...
调整数组奇偶数顺序
今天给大家分享一道题目,要求我们输入一个数组,将全部奇数放在偶数前面(无需比较大小),下面是我写的代码 这个方法比使用三个数组进行数据传输要节省不少程序运行时间,缺点是使用了较多的while循环…...
Electron调用nodejs的cpp .node扩展【非安全】
Electron调用nodejs的cpp .node扩展【非安全】 环境: electron: 30.1.1 nodejs: 20.14.0前言 Electron中可以非常容易的调用nodejs的js代码,但是对于cpp .node扩展需要一定的配置才能调用,下面介绍一种最简单的cpp扩展的调用方法ÿ…...
一文了解AOSP是什么?
一文了解AOSP是什么? AOSP基本信息 基本定义 AOSP是Android Open Source Project的缩写,这是一个由Google维护的完全免费和开放的操作系统开发项目。它是Android系统的核心基础,提供了构建移动操作系统所需的基本组件。 主要特点 完全开源…...
ffmpeg视频边缘模糊,打造梦幻般的视觉效果!
在视频编辑的世界里,细节决定成败。边缘模糊效果是一种强大的工具,可以让你的作品瞬间提升质感。通过简单的命令,你可以轻松实现视频边缘的柔和化处理,创造出梦幻般的视觉效果。 想象一下,当你将一段普通的视频应用边…...
[Wireshark] 使用Wireshark抓包https数据包并显示为明文、配置SSLKEYLOGFILE变量(附下载链接)
前言 wireshark安装包 链接:https://pan.quark.cn/s/febb28f57c01 提取码:fUCQ 链接失效(可能会被官方和谐)可评论或私信我重发 chrome与firefox在访问https网站的时候会将密钥写入这个环境变量SSLKEYLOGFILE中,在wir…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...
「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案
在移动互联网营销竞争白热化的当下,推客小程序系统凭借其裂变传播、精准营销等特性,成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径,助力开发者打造具有市场竞争力的营销工具。 一、系统核心功能架构&…...
