跳跃表原理及实现

news2025/1/21 0:58:03

一、跳表数据结构

         跳表是有序表的一种,其底层是通过链表实现的。链表的特点是插入删除效率高,但是查找节点效率很低,最坏的时间复杂度是O(N),那么跳表就是解决这一痛点而生的。

        为了提高查询效率,我们可以给链表加上索引,利用二分查找的思路,每两个节点抽取一个索引,根据数据规模,提升索引的高度,索引的最高层级为logN,那么跳跃表支持平均0 (1ogN),这样可以快读提高节点访问速度。由于在原始链表的基础上加索引,这些索引需要额外的存储空间,所以这是典型的通过空间换时间。下图简单描述跳跃表原理:

           如果要访问8这个歌节点元素,在没有索引的情况下,需要遍历链表8次才能找到目标节点,但是通过跳表访问(1 -> 5 -> 7-> 7->7 -> 8) ,只需要访问6次,数据规模越大优势越明显。

          对于提取索引,理论上每隔两个元素生成一个索引节点,但是在具体情况下,链表是动态的,删除和增加节点的时机很难确定,通过两个节点维护索引的方式开销成本很大。那么如何添加索引,一个新增节点要不要加索引,索引加到第几层,为了解决这个问题,可以通过投掷硬币的方式(随机数模2),连续投掷正面几次,那么这个次数就是索引的层级。

二、跳表代码实现

  1、跳表结构、操作函数声明
#ifndef SKIPLINKLIST_H__
#define SKIPLINKLIST_H__

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <math.h>
#include <unistd.h>

#define MAX_LEVEL 8

//定义数据域
typedef  int SkipLinkListData;

typedef  struct skiplinklistnode
{
    int  level;
    SkipLinkListData  data;
    struct skiplinklistnode*  next;
    struct skiplinklistnode*  down;

} SkipLinkListNode;

/**
 * 创建链表节点
*/
SkipLinkListNode* create_skiplinklist_node(SkipLinkListData data,int level);

/**
 * 插入节点
*/
void  insert_skiplinklist_node(SkipLinkListNode* head,SkipLinkListData data);

/**
 * 维护索引
*/
void create_skiplinklist_index(SkipLinkListNode** index,SkipLinkListNode* node);

/**
 * 随机数投硬币获取索引层高
*/
int   random_skiplinklistnode_level();


/**
 * 遍历跳表
*/
void  show_skiplinglistnode_all(SkipLinkListNode* head);

/**
 * 查询节点
*/
SkipLinkListNode*  search_skiplinklistnode(SkipLinkListNode* head,SkipLinkListData data);

/**
 * 删除跳表元素 重组索引  s
 * 删除的过程其实也是查找
*/
void    delete_skiplinklistnode(SkipLinkListNode* head,SkipLinkListData data);

#endif
2、跳表增删查操作定义

#include "./skiplinklist.h"


SkipLinkListNode* create_skiplinklist_node(SkipLinkListData data,int level){
   SkipLinkListNode*  node = (SkipLinkListNode*) malloc(sizeof(SkipLinkListNode));
   if(node==NULL)
   {
    perror("create node  fail");
    return NULL;
   }
   node->level = level;
   node->data =  data;
   node->next = NULL;
   node->down = NULL;
   return node;
}

void insert_skiplinklist_node(SkipLinkListNode *head, SkipLinkListData data)
{
  SkipLinkListNode *down_ptr = head->down;
  if (down_ptr == NULL)
  {
    head->down =  create_skiplinklist_node(data, 0);
    return;
  }
  int level = random_skiplinklistnode_level(); 
  if(down_ptr->level<level)
  {
     level = down_ptr->level +1;
  }
  SkipLinkListNode* index_node = NULL;
   /// 当前层级小于随机高度时候,需要升级索引
  if(down_ptr->level<level){
      /// 向上升级一层索引
      level = down_ptr->level +1;
      SkipLinkListNode* down_node = create_skiplinklist_node(down_ptr->data,level);
      index_node = create_skiplinklist_node(data,level);
      down_node->next = index_node;
      down_node->down = down_ptr;
      head->down = down_node;
  } 
   /// 搜索节点
  while (down_ptr!= NULL && down_ptr->data<=data && down_ptr->level>0)
  {
      SkipLinkListNode* next_ptr  = down_ptr->next;
      // 查找当前层级索引,定位到离当前数值的最大索引值
      while (next_ptr != NULL && next_ptr->data<=data && next_ptr->next!=NULL)
      {
         next_ptr = next_ptr->next;
      }
      /// 维护索引
      if(down_ptr->level<=level){
          SkipLinkListNode* new_node = create_skiplinklist_node(data, down_ptr->level);
          if(next_ptr==NULL)
          {
            /// 如果当前层索引到达最后一个值,则跳到下一层索引
            down_ptr->next=new_node;
          }
          else
          {
           new_node->next = next_ptr->next;
           next_ptr->next = new_node; 
          }
          create_skiplinklist_index(&index_node,new_node);
      }
      ///跳转下一层索引
      down_ptr = next_ptr != NULL?next_ptr->down:down_ptr->down;    
    }
    /// 遍历链表  数据插入链表
    while (down_ptr != NULL&&down_ptr->next!=NULL&&down_ptr->next->data<data)
    {
         down_ptr = down_ptr->next;
    }
    SkipLinkListNode*  node = create_skiplinklist_node(data, 0);
    SkipLinkListNode*  next_node = down_ptr->next;
    down_ptr->next = node;
    node->next = next_node;
    if(index_node!=NULL)
    {
      create_skiplinklist_index(&index_node,node);
    }
}

void create_skiplinklist_index(SkipLinkListNode** index_node,SkipLinkListNode* new_node)
{
  if ((*index_node) == NULL)
  {
    (*index_node) = new_node;
  }
  else
  {
    SkipLinkListNode* tmp_node = (*index_node);
    while (tmp_node != NULL)
    {
      if (tmp_node->down == NULL)
      {
        tmp_node->down = new_node;
        break;
      }
      tmp_node = tmp_node->down;
    }
  }
}

int  random_skiplinklistnode_level()
{
   int level = 0;
   int mod = 2;
   while (rand() % mod == 0 )
   {
     level++;
   }
   return level>=MAX_LEVEL?MAX_LEVEL:level;
}

void  show_skiplinglistnode_all(SkipLinkListNode* head)
{
 SkipLinkListNode*  down_ptr = head->down;
 while (down_ptr!=NULL)
 {
    if(down_ptr->level==0)
    {
        printf("原 始链表: %d ",down_ptr->data);
    }
    else
    {
       printf("第%d层索引: %d ",down_ptr->level,down_ptr->data);
    }
   
    SkipLinkListNode*  next_ptr = down_ptr->next;
    while (next_ptr!=NULL)
    {
       printf("%d ",next_ptr->data);
       next_ptr = next_ptr->next;
    }
    down_ptr= down_ptr->down; 
    printf("\n");
 }
  printf("\n");
}

SkipLinkListNode*  search_skiplinklistnode(SkipLinkListNode* head,SkipLinkListData data)
{
    SkipLinkListNode* down_ptr =  head->down;
    /// 索引查找
    while (down_ptr!=NULL && down_ptr->data<=data && down_ptr->level>0)
    {
      printf("遍历第%d层次节点:%d\n",down_ptr->level,down_ptr->data);
      if(down_ptr->next!=NULL && down_ptr->next->data>data)
      {
         down_ptr = down_ptr->down;
         continue;
      }
      SkipLinkListNode* next_ptr  = down_ptr->next;
      while (next_ptr != NULL && next_ptr->data<=data && next_ptr->next!=NULL&& next_ptr->next->data<=data)
      {
         next_ptr = next_ptr->next;
         printf("遍历第%d层次节点:%d\n",next_ptr->level,next_ptr->data);
      }
      ///跳转下一层索引
      down_ptr = next_ptr != NULL?next_ptr->down:down_ptr->down;   
    }
    //到达底层链表 遍历目标值
    while (down_ptr!=NULL)
    {
       if(down_ptr->data==data)
       {
          printf("遍历第%d层次节点,命中目标%d\n",down_ptr->level,down_ptr->data);
          return down_ptr;
       }
       down_ptr =  down_ptr->next;
    }
    printf("遍历结束目标节点%d不存在\n",data);
    printf("\n");
    return NULL;
}

void delete_skiplinklistnode(SkipLinkListNode *head, SkipLinkListData data)
{
   printf("删除元素开始\n");
  SkipLinkListNode *down_ptr = head->down;
  while (down_ptr != NULL && down_ptr->data < data && down_ptr->level > 0)
  {
    printf("遍历第%d层次节点:%d\n", down_ptr->level, down_ptr->data);
    if (down_ptr->next != NULL && down_ptr->next->data>=data)
    {
      /// 处理要删除的节点存在索引节点
      if(down_ptr->next->data==data)
      {
         SkipLinkListNode* index_ptr = down_ptr->next;
         down_ptr->next = down_ptr->next->next;
         printf("删除第%d层索引%d\n",index_ptr->level,index_ptr->data);
         free(index_ptr);
      }
      down_ptr = down_ptr->down;
      continue;
    }
    SkipLinkListNode *next_ptr = down_ptr->next;
    while (next_ptr != NULL && next_ptr->data < data && next_ptr->next != NULL && next_ptr->next->data <= data)
    {
      if(next_ptr->next->data==data)
      {
        SkipLinkListNode*  index_node= next_ptr->next;
        next_ptr->next = next_ptr->next->next;
        free(index_node);
        continue;
      }
      next_ptr = next_ptr->next;
      printf("遍历第%d层次节点:%d\n", next_ptr->level, next_ptr->data);
    }
    /// 跳转下一层索引
    down_ptr = next_ptr != NULL ? next_ptr->down : down_ptr->down;
  }

  while (down_ptr!=NULL)
  {
     if(down_ptr->next!=NULL && down_ptr->next->data==data)
     {
        SkipLinkListNode* traget_node =  down_ptr->next;
        down_ptr->next =  down_ptr->next->next;
        free(traget_node);
     }
     down_ptr=down_ptr->next;
  }
  printf("删除元素结束\n");

}

三、跳表测试


void  test_skiplinklist()
{

 SkipLinkListNode*  head = create_skiplinklist_node(0,0);
 SkipLinkListData  i;
 int c = 30;
 for(i=1;i<c;i++)
 {
    insert_skiplinklist_node(head,i);   
 }
 
 show_skiplinglistnode_all(head);
 search_skiplinklistnode(head,28);
 delete_skiplinklistnode(head,15);
 show_skiplinglistnode_all(head);
 
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1347864.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ORACLE Primavera Unifier v23.12 最新虚拟机(VM)分享下载

引言 根据上周的计划&#xff0c;我近日简单制作了一个基于ORACLE Primavera Unifier 最新版23.12的虚拟机演示环境&#xff0c;里面包括了unifier的全套系统服务 此虚拟系统环境仅用于演示、培训和测试目的。如要在生产环境中使用此虚拟机&#xff0c;请您与Oracle 销售代表联…

pngPackerGUI_V2.0是什么工具?

pngPackerGUI_V2.0是什么工具&#xff1f; png图片打包plist工具&#xff0c;手把手教你使用pngPackerGUI_V2.0此软件是在pngpacker_V1.1软件基础之后&#xff0c;开发的界面化操作软件&#xff0c;方便不太懂命令行的小白快捷上手使用。1.下载并解压缩软件&#xff0c;得到如…

关于“Python”的核心知识点整理大全53

目录 18.2.7 Django shell 注意 18.3 创建网页&#xff1a;学习笔记主页 18.3.1 映射 URL urls.py urls.py 注意 18.3.2 编写视图 views.py 18.3.3 编写模板 index.html 往期快速传送门&#x1f446;&#xff08;在文章最后&#xff09;&#xff1a; 感谢大家的支…

VitulBox中Ubuntu虚拟机安装JAVA环境——备赛笔记——2024全国职业院校技能大赛“大数据应用开发”赛项

前言 在进行之后操作是请下载好JDK&#xff0c;之后的内容是以Ubuntu虚拟机中安装java环境续写。 提示&#xff1a;以下操作是在虚拟机hadoop用户下操作的&#xff0c;并为安装java环境作准备 一、更新APT 为了确保Hadoop安装过程顺利进行&#xff0c;建议用hadoop用户登录…

MCS接口技术----定时/计数,中断

目录 一.中断系统相关寄存器 1.51单片机中断系统的总体结构&#xff1a; 2.中断源的中断级别&#xff08;由高到低&#xff09;&#xff1a; 3.与中断有关的四个寄存器&#xff1a; &#xff08;1&#xff09;TCON---定时控制寄存器 &#xff08;2&#xff09;IE---中断允…

一二三应用开发平台文件处理设计与实现系列之3——后端统一封装设计与实现

背景 前面介绍了前端通过集成vue-simple-uploader实现了文件的上传&#xff0c;今天重点说一下后端的设计与实现。 功能需求梳理 从功能角度而言&#xff0c;实际主要就两项&#xff0c;一是上传&#xff0c;二是下载。其中上传在文件体积较大的情况下&#xff0c;为了加快上…

2013年第二届数学建模国际赛小美赛B题寄居蟹进化出人类的就业模式解题全过程文档及程序

2013年第二届数学建模国际赛小美赛 B题 寄居蟹进化出人类的就业模式 原题再现&#xff1a; 寄居蟹是美国最受欢迎的宠物品种&#xff0c;依靠其他动物的壳来保护。剥去寄居蟹的壳&#xff0c;你会看到它柔软、粉红色的腹部卷曲在头状的蕨类叶子后面。大多数寄居蟹喜欢蜗牛壳&…

Java集合/泛型篇----第五篇

系列文章目录 文章目录 系列文章目录前言一、说说LinkHashSet( HashSet+LinkedHashMap)二、HashMap(数组+链表+红黑树)三、说说ConcurrentHashMap前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通…

加强->servlet->tomcat

0什么是servlet jsp也是servlet 细细体会 Servlet 是 JavaEE 的规范之一&#xff0c;通俗的来说就是 Java 接口&#xff0c;将来我们可以定义 Java 类来实现这个接口&#xff0c;并由 Web 服务器运行 Servlet &#xff0c;所以 TomCat 又被称作 Servlet 容器。 Servlet 提供了…

数据结构: 位图

位图 概念 用一个bit为来标识数据在不在 功能 节省空间快速查找一个数在不在一个集合中排序 去重求两个集合的交集,并集操作系统中的磁盘标记 简单实现 1.设计思想:一个bit位标识一个数据, 使用char(8bit位)集合来模拟 2.预备工作:a.计算这个数在第几个char b.是这个ch…

「实验记录」CS144 Lab1 StreamReassembler

目录 一、Motivation二、SolutionsS1 - StreamReassembler的对外接口S2 - push_substring序列写入ByteStream 三、Result四、My Code五、Reference 一、Motivation 我们都知道 TCP 是基于字节流的传输方式&#xff0c;即 Receiver 收到的数据应该和 Sender 发送的数据是一样的…

C#-CSC编译环境搭建

一.Microsoft .NET Framework 确保系统中安装Microsoft .NET Framework相关版本下载 .NET Framework 4.7 | 免费官方下载 (microsoft.com)https://dotnet.microsoft.com/zh-cn/download/dotnet-framework/net47 二.编译环境搭建 已经集成编译工具csc.exe,归档至gitcode,实现us…

L1-076:降价提醒机器人

题目描述 小 T 想买一个玩具很久了&#xff0c;但价格有些高&#xff0c;他打算等便宜些再买。但天天盯着购物网站很麻烦&#xff0c;请你帮小 T 写一个降价提醒机器人&#xff0c;当玩具的当前价格比他设定的价格便宜时发出提醒。 输入格式&#xff1a; 输入第一行是两个正整数…

数据隐私:技术和法律的双重挑战

当前&#xff0c;数据已成为企业和个人最宝贵的资产之一。然而&#xff0c;随着数据的广泛收集和共享&#xff0c;数据隐私问题也日益突出。保护个人信息的隐私不仅是法律规定的义务&#xff0c;也是维护社会公正、保护个人权益的必要措施。本文将从数据隐私的概念、重要性、面…

Linux学习第48天:Linux USB驱动试验:保持热情,保持节奏,持续学习是作为一个技术人员应有的基本素质和要求

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 最近更新的速度和频率大不如以前&#xff0c;主要原因还是自己有些懈怠了。学习是一个持续努力的过程&#xff0c;一旦中断&#xff0c;再想保持以往的状态可能要…

《MySQL系列-InnoDB引擎01》MySQL体系结构和存储引擎

文章目录 第一章 MySQL体系结构和存储引擎1 数据库和实例2 MySQL配置文件3 MySQL数据库路径4 MySQL体系结构5 MySQL存储引擎5.1 InnoDB存储引擎5.2 MyISAM存储引擎5.3 NDB存储引擎5.4 Memory存储引擎5.5 Archive存储引擎5.6 Federated存储引擎 6 连接MySQL6.1 TCP/IP6.2 命名管…

MySQL数据库学习一

1 什么是数据库的事务&#xff1f; 1.1 事务的典型场景 在项目里面&#xff0c;什么地方会开启事务&#xff0c;或者配置了事务&#xff1f;无论是在方法上加注解&#xff0c;还 是配置切面。 <tx:advice id"txAdvice" transaction-manager"transactionMa…

数据结构式新年贺词

冒泡排序&#xff0c;选择排序&#xff0c;插入排序&#xff0c;快速排序&#xff0c;堆排序&#xff0c;归并排序&#xff0c;希尔排序&#xff0c;桶排序&#xff0c;基数排序新年帮您排忧解难。 有向图&#xff0c;无向图&#xff0c;有环图&#xff0c;无环图&#xff0c;…

回溯法解决n皇后问题(迭代版)

n皇后问题的关键在于judge函数&#xff0c;判断当前的情况是否合法 1.x[i]x[k]说明有两个皇后处于同一列&#xff0c;不符合 2.x[k]-x[i]k-i&#xff1a; 由于k-i是固定的&#xff0c;假设k3,i2,那么k-i1, 如果x[k]-x[i]1, 说明第k个皇后在第i个皇后右…

PostgreSQL16.1(Windows版本)

1、卸载原有的PostgreSQL &#xfeff; &#xfeff; 点击Next即可。 &#xfeff;&#xfeff; 点击OK即可。 卸载完成。 2、安装 &#xff08;1&#xff09; 前两部直接Next&#xff0c;第二部可以换成自己想要安装的路径。 &#xff08;2&#xff09; 直接点击Next。…