当前位置: 首页 > news >正文

C++跳跃表个人理解

一、概念

跳跃表是一种基于有序链表的扩展,简称跳表,其就是使用关键节点作为索引的一种数据结构。

可以通过以下方式更快查找到有序链表的某一节点:

利用类似索引的思想,提取出链表中的部分关键节点。比如,给定一个长度为7的有序链表,节点值依次是1->2->3->5->6->7->8,那么我们可以取出所有值为奇数节点的作为关键点。

此时如果要插入一个值是4的新节点,不再需要和原节点8,7,6,5,3逐一比较,只需要比较关键节点8,5,3.

确定了新节点在关键节点中的位置(3和5之间),就可以回到原链表,迅速定位到对应的位置,然后进行插入。

 

 多层关键节点多层索引

对于跳跃表来说,当然不能只会有一层索引节点,那么可以进一步提取索引,在索引层中提取出一层新的索引。

有了二级索引之后,新的节点可以先和2级索引进行比较,确定大体范围;然后再和1级索引比较;最后回到原链表,找到并插入对应位置。

 层级极限是什么?

当节点足够多的时候,不止能提取出二级索引,还可以向高层次提取,保证每一层是上一层节点数的一半。

提取的极限:同一层只有两个节点的时候,因为一个节点没有比较的意义。

怎么从新节点当中选取一部分提到上一层?

当大量的新节点通过逐层比较,最终插入到原链表之后,上层的索引节点会变得不够用。这时候需要从新节点当中选取一部分提到上一层。

使用随机决定新节点是否提升层级,每次向上提升一层的概率是百分之50.

为什么要随机决定新节点是否层级提升?

(1)跳跃表删除和添加节点是不可预测的,很难用一种有效地算法来保证跳跃表的索引部分始终均匀。

(2)采用随机法虽然不能保证索引绝对均匀分布,却可以让大体趋于均匀。

二、时间复杂度分析

跳跃表将新节点(删除节点)和各层索引节点逐一进行比较,确定原链表的插入或者删除位置。时间复杂度为O(logn)。

跳跃表的插入和删除操作需要的时间复杂度和原始链表一样,都为O(1)。

三、跳跃表的特征

一个完整的跳跃表,应该具有以下特征:

1、一个跳表应该有几个层(level)组成。

2、跳表的第一层包含所有的元素。

3、每一层都是一个有序的链表。

4、如果元素x出现在第i层,则所有比i小的层都包含x。

5、每个节点包含key及其对应的value和一个指向同一层链表的下个节点的指针数组(next[i])。

四、跳跃表和红黑树性能比较

目前,经常使用的平衡数据结构有:B树、红黑树、AVL树等。跳跃表是平衡树一种替代的数据结构,但是和红黑树不相同的是,跳跃表对于平衡的实现是基于一种随机化的算法,跳跃表的插入和删除工作是比较简单的。

1、查询性能:

跳跃表和红黑树的查询时间复杂度都为O(logn)。

2、插入和删除性能:

跳跃表在进行插入和删除操作时,相对来说,更容易调整结构,也就是可能需要在索引层中增加或者删除相应节点。

红黑树在进行插入和删除操作时,就需要进行旋转或者颜色调度操作,相对复杂一些。

3、空间复杂度:

跳跃表的空间复杂度相对高一些,需要额外的空间来存储多层指针。平均情况下O(n),最糟糕的情况下O(nlogn)。

红黑树空间开销相对跳跃表来说会低。O(n)。

4、应用场景:

如果要在高并发的场景下,频繁进行插入和删除节点,那么跳跃表可能更合适一些。

如果对空间性能要求较高,那么可以去使用红黑树。

五、代码实现

#include<iostream>
#include<time.h>
#include<assert.h>
#include<stdlib.h>
using namespace std;
#define MAX_L 5//跳跃表最大层数//节点结构
struct Node
{int key;//节点的值Node* next[];//多层链表节点
};
//跳跃表结构
struct Skiplist
{int level;//跳跃表层数Node* head;
};
//创建节点
Node* CreateNode(int level, int key)
{Node* p = (Node*)malloc(sizeof(Node) + level * sizeof(Node*));if (nullptr == p){return nullptr;}for (int i = 0; i < level; ++i){p->next[i] = nullptr;}p->key = key;return p;
}
//创建表
Skiplist* CreateList()
{Skiplist* s = (Skiplist*)malloc(sizeof(Skiplist));if (nullptr == s){return nullptr;}s->level = 0;Node* nh = CreateNode(MAX_L + 1, 0);if (nullptr == nh){free(s);return nullptr;}s->head = nh;return s;
}
//生成新节点的级数
int random()
{int level = 0;while (level <= MAX_L && rand() < RAND_MAX / 2){++level;}return level;
}
//向跳跃表中插入元素
bool InsertNode(Skiplist* s, int key)
{if (nullptr == s) return false;//第一步,查找到在每层待插入位置,更新update数组Node* update[MAX_L + 1] = {};Node* q = nullptr, * p = s->head;//找到每一层插入前的一个节点,更新update数组for (int i = s->level; i >= 0; --i){while (p->next[i] != nullptr && p->next[i]->key < key){p = p->next[i];}update[i] = p;}p = p->next[0];if (p != nullptr && p->key == key){return true;}//第二步,随机产生一个层数int newlevel = random();//新产生的节点层数比原跳跃表大if (newlevel > s->level){for (int i = s->level + 1; i <= newlevel; ++i){update[i] = s->head;}s->level = newlevel;}//第三步,从高往下插入p = CreateNode(newlevel + 1, key);if (nullptr == p){return false;}//根据update数组在每一层中插入新节点for (int i = newlevel; i >= 0; --i){p->next[i] = update[i]->next[i];update[i]->next[i] = p;}return true;
}
//删除跳跃表中的元素
bool DeleteNode(Skiplist* s, int key)
{Node* update[MAX_L + 1] = {};Node * p = s->head;//找到每一层待删除元素前的一个节点,放入update数组中for (int i = s->level; i >= 0; --i){while (p->next[i] != nullptr && p->next[i]->key < key){p = p->next[i];}update[i] = p;}Node* q = p->next[0];assert(q != nullptr);//判断q是否为待删除的节点 if (q==nullptr && q->key != key){return false;}for (int i = s->level; i >= 0; --i){if (update[i]->next[i]==q){update[i]->next[i] = q->next[i];if (s->head->next[i] == nullptr){s->level--;}}}free(q);return true;
}
void Print(Skiplist* s)
{Node* p = nullptr;for (int i = s->level; i >= 0; --i){p = s->head->next[i];cout << "level:" << i << endl;while (p != nullptr){cout << "key:" << p->key << " ";p = p->next[i];}cout << endl;}
}
void FreeList(Skiplist* s)
{if (nullptr == s){return;}Node* q = s->head;Node* next = nullptr;while (q != nullptr){next = q->next[0];free(q);q = next;}free(s);
}
int main()
{Skiplist* mylist = CreateList();for (int i = 1; i < 5; ++i){InsertNode(mylist, i);}DeleteNode(mylist, 3);Print(mylist);free(mylist);return 0;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 如何设计一个测试用例
  • 计算两个时间之间有几个自然月
  • 量化小白也能自动化挖掘出6万+因子
  • 5分钟完成视频会议私有化部署
  • 类和对象的深入了解6
  • 【C语言】简易版扫雷游戏(数组、函数的练习)
  • 05-ArcGIS For JavaScript-RenderNode后处理效果
  • [012-1].第12节:Mysql的配置文件的使用
  • ubuntu安装workon
  • MyBatis缓存
  • DataKit之OpenGauss数据迁移工具
  • 大数据技术基础编程、实验和案例----大数据课程综合实验案例
  • SpringBoot如何实现简单的跨域配置
  • (七)Appdesigner-初步入门及常用组件的使用方法说明
  • 程序员修炼之路
  • AHK 中 = 和 == 等比较运算符的用法
  • CSS3 变换
  • HTTP中的ETag在移动客户端的应用
  • iOS筛选菜单、分段选择器、导航栏、悬浮窗、转场动画、启动视频等源码
  • Java 最常见的 200+ 面试题:面试必备
  • jquery cookie
  • JS学习笔记——闭包
  • laravel with 查询列表限制条数
  • Phpstorm怎样批量删除空行?
  • Promise初体验
  • python 装饰器(一)
  • spring + angular 实现导出excel
  • tweak 支持第三方库
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 二维平面内的碰撞检测【一】
  • 快速体验 Sentinel 集群限流功能,只需简单几步
  • 前端相关框架总和
  • 分布式关系型数据库服务 DRDS 支持显示的 Prepare 及逻辑库锁功能等多项能力 ...
  • 智能情侣枕Pillow Talk,倾听彼此的心跳
  • # 日期待t_最值得等的SUV奥迪Q9:空间比MPV还大,或搭4.0T,香
  • # 消息中间件 RocketMQ 高级功能和源码分析(七)
  • #14vue3生成表单并跳转到外部地址的方式
  • #ifdef 的技巧用法
  • (+3)1.3敏捷宣言与敏捷过程的特点
  • (14)目标检测_SSD训练代码基于pytorch搭建代码
  • (152)时序收敛--->(02)时序收敛二
  • (39)STM32——FLASH闪存
  • (python)数据结构---字典
  • (STM32笔记)九、RCC时钟树与时钟 第二部分
  • (ZT) 理解系统底层的概念是多么重要(by趋势科技邹飞)
  • (第61天)多租户架构(CDB/PDB)
  • (计算机网络)物理层
  • (牛客腾讯思维编程题)编码编码分组打印下标题目分析
  • (转)http协议
  • (转载)Google Chrome调试JS
  • (轉貼) VS2005 快捷键 (初級) (.NET) (Visual Studio)
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • .halo勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET Core 发展历程和版本迭代
  • .net core 使用js,.net core 使用javascript,在.net core项目中怎么使用javascript