跳表Java

news2024/9/20 14:34:10

跳表(Skip List)是一种用于有序数据存储的数据结构,它在链表的基础上增加了多级索引,从而提高了查找、插入和删除操作的效率。跳表的平均时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn),与平衡二叉搜索树(如 AVL 树、红黑树)相当,但实现和维护相对简单。

在这里插入图片描述

跳表的结构

跳表由多层链表组成,每一层都是一个有序链表。最底层(Level 0)包含所有元素,每一层的元素是下一层元素的子集。通过在多层链表中进行跳跃,可以快速定位到目标元素。

  • Level 0:包含所有元素的有序链表。
  • Level 1:包含部分元素的有序链表,通常是 Level 0 的元素的子集。
  • Level 2:包含更少元素的有序链表,通常是 Level 1 的元素的子集。

跳表的操作

  1. 查找
    从最高层开始,逐层向下查找,直到找到目标元素或确定元素不存在。每层查找时,如果当前元素小于目标元素,则向右移动;否则,向下移动到下一层。

  2. 插入
    首先找到插入位置,然后在每一层随机决定是否插入新元素。插入时,需要更新相应层的前后指针。

  3. 删除
    首先找到要删除的元素,然后在每一层删除该元素,并更新相应层的前后指针。

跳表的优点

  • 高效:跳表的查找、插入和删除操作的平均时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn)
  • 简单:相比于平衡二叉树,跳表的实现和维护相对简单。
  • 动态性:跳表可以动态调整索引层数,适应数据的变化。

跳表的实现

以下是一个简单的跳表实现示例(Java 代码):

import java.util.Random;

class SkipListNode {
    int value;
    SkipListNode[] forward;

    public SkipListNode(int value, int level) {
        this.value = value;
        this.forward = new SkipListNode[level + 1];
    }
}

public class SkipList {
    private static final int MAX_LEVEL = 16;
    private final SkipListNode head = new SkipListNode(-1, MAX_LEVEL);
    private int level = 0;
    private final Random random = new Random();

    public void insert(int value) {
        SkipListNode[] update = new SkipListNode[MAX_LEVEL + 1];
        SkipListNode current = head;

        for (int i = level; i >= 0; i--) {
            while (current.forward[i] != null && current.forward[i].value < value) {
                current = current.forward[i];
            }
            update[i] = current;
        }

        int newLevel = randomLevel();
        if (newLevel > level) {
            for (int i = level + 1; i <= newLevel; i++) {
                update[i] = head;
            }
            level = newLevel;
        }

        SkipListNode newNode = new SkipListNode(value, newLevel);
        for (int i = 0; i <= newLevel; i++) {
            newNode.forward[i] = update[i].forward[i];
            update[i].forward[i] = newNode;
        }
    }

    public boolean search(int value) {
        SkipListNode current = head;
        for (int i = level; i >= 0; i--) {
            while (current.forward[i] != null && current.forward[i].value < value) {
                current = current.forward[i];
            }
        }
        current = current.forward[0];
        return current != null && current.value == value;
    }

    public void delete(int value) {
        SkipListNode[] update = new SkipListNode[MAX_LEVEL + 1];
        SkipListNode current = head;

        for (int i = level; i >= 0; i--) {
            while (current.forward[i] != null && current.forward[i].value < value) {
                current = current.forward[i];
            }
            update[i] = current;
        }

        current = current.forward[0];
        if (current != null && current.value == value) {
            for (int i = 0; i <= level; i++) {
                if (update[i].forward[i] != current) {
                    break;
                }
                update[i].forward[i] = current.forward[i];
            }
            while (level > 0 && head.forward[level] == null) {
                level--;
            }
        }
    }

    private int randomLevel() {
        int newLevel = 0;
        while (random.nextInt(2) == 0 && newLevel < MAX_LEVEL) {
            newLevel++;
        }
        return newLevel;
    }

    public static void main(String[] args) {
        SkipList skipList = new SkipList();
        skipList.insert(1);
        skipList.insert(2);
        skipList.insert(3);

        System.out.println(skipList.search(1)); // true
        System.out.println(skipList.search跳表(Skip List)是一种基于链表的数据结构,旨在提高有序链表的查找效率。它通过在链表上建立多级索引,使得查找、插入和删除操作的时间复杂度从 $O(n)$ 降低到 $O(\log n)$,接近于平衡树的性能,但实现和维护相对简单。

### 跳表的结构

跳表由多层链表组成,每一层都是一个有序链表。最底层(Level 0)包含所有元素,而每一层的元素是下一层元素的子集。每个元素在跳表中的层数是随机确定的,通常遵循几何分布。

- **Level 0**:包含所有元素。
- **Level 1**:包含部分元素,作为 Level 0 的索引。
- **Level 2**:包含更少的元素,作为 Level 1 的索引。
- **...**
- **Level k**:包含最少的元素,作为 Level k-1 的索引。

### 跳表的操作

1. **查找**:
   从最高层开始,逐层向下查找,直到找到目标元素或确定元素不存在。每一层的查找都是线性的,但由于层数较少,总体查找效率较高。

2. **插入**:
   插入元素时,首先确定该元素在每一层的位置,然后在相应层次插入该元素。插入时需要更新相应的索引。

3. **删除**:
   删除元素时,首先在每一层查找到该元素的位置,然后在相应层次删除该元素。删除时需要更新相应的索引。

### 跳表的时间复杂度

- **查找**:$O(\log n)$
- **插入**:$O(\log n)$
- **删除**:$O(\log n)$

### 跳表的优点

- **简单性**:跳表的实现和维护相对简单,代码量较少。
- **高效性**:跳表的查找、插入和删除操作的时间复杂度接近于平衡树。
- **动态性**:跳表可以动态调整,不需要复杂的旋转操作。

### 跳表的实现示例(Java)

以下是一个简单的跳表实现示例:

```java
import java.util.Random;

class SkipListNode {
    int value;
    SkipListNode[] forward;

    public SkipListNode(int value, int level) {
        this.value = value;
        this.forward = new SkipListNode[level + 1];
    }
}

public class SkipList {
    private static final int MAX_LEVEL = 16;
    private SkipListNode head;
    private int level;
    private Random random;

    public SkipList() {
        this.head = new SkipListNode(-1, MAX_LEVEL);
        this.level = 0;
        this.random = new Random();
    }

    private int randomLevel() {
        int lvl = 0;
        while (random.nextInt(2) == 1 && lvl < MAX_LEVEL) {
            lvl++;
        }
        return lvl;
    }

    public void insert(int value) {
        SkipListNode[] update = new SkipListNode[MAX_LEVEL + 1];
        SkipListNode x = head;
        for (int i = level; i >= 0; i--) {
            while (x.forward[i] != null && x.forward[i].value < value) {
                x = x.forward[i];
            }
            update[i] = x;
        }
        int lvl = randomLevel();
        if (lvl > level) {
            for (int i = level + 1; i <= lvl; i++) {
                update[i] = head;
            }
            level = lvl;
        }
        x = new SkipListNode(value, lvl);
        for (int i = 0; i <= lvl; i++) {
            x.forward[i] = update[i].forward[i];
            update[i].forward[i] = x;
        }
    }

    public boolean search(int value) {
        SkipListNode x = head;
        for (int i = level; i >= 0; i--) {
            while (x.forward[i] != null && x.forward[i].value < value) {
                x = x.forward[i];
            }
        }
        x = x.forward[0];
        return x != null && x.value == value;
    }

    public void delete(int value) {
        SkipListNode[] update = new SkipListNode[MAX_LEVEL + 1];
        SkipListNode x = head;
        for (int i = level; i >= 0; i--) {
            while (x.forward[i] != null && x.forward[i].value < value) {
                x = x.forward[i];
            }
            update[i] = x;
        }
        x = x.forward[0];
        if (x != null && x.value == value) {
            for (int i = 0; i <= level; i++) {
                if (update[i].forward[i] != x) {
                    break;
                }
                update[i].forward[i] = x.forward[i];
            }
            while (level > 0 && head.forward[level] == null) {
                level--;
            }
        }
    }

    public static void main(String[] args) {
        SkipList skipList = new SkipList();
        skipList.insert(1);
        skipList.insert(2);
        skipList.insert(3);
        System.out.println(skipList.search(1)); // true
        System.out.println(skipList.search(4)); // false
        skipList.delete(1);
        System.out.println(skipList.search(1)); // false
    }
}

这个示例展示了跳表的基本操作,包括插入、查找和删除。通过使用多级索引,跳表可以在保持简单性的同时提供高效的查找、插入和删除操作。

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

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

相关文章

编程小白如何成为大神?——新生入门指南

编程小白如何成为大神&#xff1f;——新生入门指南 作为一名已经从985高校毕业的研究生&#xff0c;我深刻体会到编程已成为当代大学生的必备技能。无论是为了学术研究&#xff0c;还是未来的职业发展&#xff0c;掌握编程都能为我们提供更多的机会和竞争优势。然而&#xff…

vscode启动不了的问题解决

1、安全模式下启动vscode从中查看日志&#xff1a; code --verbose at Ce.d (C:\Users\yonghu\AppData\Local\Programs\Microsoft VS Code\resources\app\out\vs\code\electron-main\main.js:116:3783)at Ce.a (C:\Users\yonghu\AppData\Local\Programs\Microsoft VS Code\res…

ts保姆级学习指南

什么是 TypeScript&#xff1f; TypeScript&#xff0c;简称 ts&#xff0c;是 JavaScript 的超集&#xff0c;而且它最大的特点之一就是引入了静态类型支持。这意味着开发者可以在 TypeScript 中定义变量、函数参数等的类型&#xff0c;编译器会在编译时进行类型检查&#xf…

Ubuntu配置Ngbatis学习环境

引言 经过考虑&#xff0c;我感觉与NebulaGraph交互的ORM框架还是Ngbatis好。因为现在这个框架开发的比较完善&#xff0c;而且还在不断更新&#xff0c;社区活跃的用户多。从今日开始学习&#xff0c;首先要配置一下环境。 1.安装maven和jdk 选择的版本是maven3.8和jdk17.以…

iPhone可运行的谷歌Gemma 2 2B模型,性能超GPT-3.5

在数字化浪潮的推动下&#xff0c;人工智能&#xff08;AI&#xff09;正成为塑造未来的关键力量。硅纪元视角栏目紧跟AI科技的最新发展&#xff0c;捕捉行业动态&#xff1b;提供深入的新闻解读&#xff0c;助您洞悉技术背后的逻辑&#xff1b;汇聚行业专家的见解&#xff0c;…

关于inet_addr()中的参数不能是 sring类型的 只能是 string类型变量.c_str()

源码展示&#xff1a; extern in_addr_t inet_addr (const char *__cp) __THROW inet_addr中的参数是const char *类型的 定义一个string 类型的ip 使用这个inet_addr()接口 local.sin_addr.s_addr inet_addr(ip_.c_str()); local.sin_addr.s_addr inet_addr(&ip_);…

ELK对业务日志进行收集

ELK对业务日志进行收集 下载httpd 进到文件设置收集httpd的文件进行 设置 编辑内容 用于收集日志的内容 将日志的内容发送到实例当中 input {file{path > /etc/httpd/logs/access_logtype > "access"start_position > "beginning"}file{path &g…

基于SpringBoot+Vue的健身俱乐部网站(带1w+文档)

基于SpringBootVue的健身俱乐部网站(带1w文档) 基于SpringBootVue的健身俱乐部网站(带1w文档) 该系统采用java技术&#xff0c;结合ssm框架使页面更加完善&#xff0c;后台使用MySQL数据库进行数据存储。系统主要分为三大模块&#xff1a;即管理员模块和用户模块、教练模块。本…

openstack之nova-conductor工作原理及常见问题处理

openstack之nova-conductor工作原理及常见问题处理 这里写目录标题 openstack之nova-conductor工作原理及常见问题处理一、简介1. 概念2. 作用3. 体系结构 二、组件1. nova-api2. nova-scheduler3. nova-compute4. nova-conductor5. nova-api-metadata6. nova-placement-api7. …

Java AI伪原创视频创作视频提取文案改写去水印系统小程序源码

&#x1f525;AI赋能创作新纪元&#xff01;伪原创视频文案提取改写去水印全能系统大揭秘 &#x1f680; 开篇&#xff1a;创意无界&#xff0c;AI来助力 在这个视觉盛行的时代&#xff0c;视频创作成为了表达自我、传递信息的重要方式。但你是否曾为寻找灵感、撰写文案、处理…

SD-WAN的两种方案及其价值

SD-WAN&#xff08;软件定义广域网&#xff09;作为一种新兴的网络架构解决方案&#xff0c;给企业网络带来了极大的灵活性和可扩展性。它允许企业以更低的成本将广泛分布的分支机构连接到数据中心或其他分支机构&#xff0c;同时还能优化网络性能和用户体验。 SD-WAN的工作原理…

C语言之“文件操作”

文章目录 1. 什么是文件&#xff1f;&#xff08;1. 为什么使用文件&#xff1f;&#xff08;2.什么是文件2.1 程序文件2.2 数据文件2.3 文件名 2. 二进制文件和文本文件&#xff1f;3. 文件的打开和关闭3.1 流和标准流3.2 文件指针(用来管理流的)3.3 文件的打开与关闭 4. 文件…

vitis (eclipse) 的Indexer不能搜索、不能跳转到函数和变量定义和声明不能打开调用层次的解决方法

在使用vitis(2021.1) 过程中&#xff0c;有一个非常方便实用的功能&#xff0c;就是在函数或变量等源代码上通过右键菜单或快捷键F3、F4、CtrlAltH&#xff0c;也可以按住Ctrl键然后鼠标停留在函数名或变量名上&#xff0c;点击出现的链接&#xff0c;可以跳转到函数或变量的定…

linux磁盘可视化分析工具

在 Linux 系统中&#xff0c;了解磁盘使用情况对于系统维护和优化至关重要。文件和目录随着时间的推移会占据大量磁盘空间&#xff0c;了解哪些部分占用的空间最多可以帮助我们更好地管理和清理磁盘。Baobab&#xff0c;也称为 GNOME Disk Usage Analyzer&#xff0c;是一款非常…

数据化信息时代中开源 AI 智能名片拓客微信小程序的角色与价值

摘要&#xff1a;本文深入探讨了数据化信息的特性&#xff0c;包括其数字化基础、多媒体表现、可转化性及增值利用特点。同时&#xff0c;着重阐述了开源 AI 智能名片拓客微信小程序在这一背景下的重要作用和独特价值&#xff0c;为信息传播与利用提供了新的视角和思路。 关键…

WCF 禁止第三方访问,避免泄露元数据信息

开发的时候&#xff0c;服务端的web.config,将httpGetEnabled和httpsGetEnabled置true&#xff0c;这个时候客户端就可以添加服务引用。开发结束后&#xff0c;部署的时候&#xff0c;将这俩配置改成false

Bug 解决 | 后端项目无法正常启动,或依赖服务连接失败

目录 1、版本问题 2、依赖项问题 明明拷贝的代码&#xff0c;为什么别人行&#xff0c;我启动就报错&#xff1f; 这篇文章我就理一下最最常见的项目启动报错的两种原因&#xff01; 1、版本问题 比如明明项目的 Java 版本是 8&#xff0c;你非得拿 5 跑&#xff1f;那不是…

python爬虫预备知识三-序列化和反序列化

序列化和反序列化 序列化是为了将内存中的数据保存在磁盘上或者用于传输&#xff0c;实现程序状态的保存和共享。反序列化反之。 序列化后的变量再被反序列化回来之后&#xff0c;两者之间已经没有任何关系。 序列化后的文件是在不同程序或者说不同语言之间传递数据的关键方…

开发助手专业版,有反编译等多种功能

软件介绍 开发助手能够用来快速调试应用以及查看手机软硬件相关信息&#xff0c;包括&#xff1a;快速打开或关闭开发者选项中的选项。 将原来几十秒的操作缩短为一次点击。包括显示布局边界&#xff0c;显示 GPU 过度绘制。显示布局更新。强制 GPU 渲染 显示 GPU 视图更新&a…

第15课 Scratch少儿编程 入门篇:师生问候

师生问候 故事背景&#xff1a; 魔法学院的期末考核刚刚考完&#xff0c;魔法老师在教室里碰到小明&#xff0c;老师问小明考的怎么样&#xff1f; 程序原理&#xff1a; 找一个教室的背景&#xff0c;小精灵角色和魔法师的角色&#xff0c;将魔法师的角色造型左右反转&…