返回顶部
首页 > 资讯 > 后端开发 > Python >Java实现跳跃表的示例详解
  • 333
分享到

Java实现跳跃表的示例详解

2024-04-02 19:04:59 333人浏览 泡泡鱼

Python 官方文档:入门教程 => 点击学习

摘要

跳表全称叫做跳跃表,简称跳表,是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表。跳表在原有的有序列表上面增加多级索引,通过索引来实现快速查找。跳表不仅能提高搜索性能,同

跳表全称叫做跳跃表,简称跳表,是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表。跳表在原有的有序列表上面增加多级索引,通过索引来实现快速查找。跳表不仅能提高搜索性能,同时也提高插入和删除的性能,Redis中的有序集合set就是用跳表实现的,面试时候也经常会问。 

这里我们原始数据个数n=10,以间隔k=2建立索引,则第一层索引10/2=5个,第二层⌈10/2^2⌉=3个,第三层⌈10/2^3⌉=2个,第四层⌈10/2^4⌉=1个。根据上图我们来分析一下,跳表的结构是一棵树(除原始数据层外),树的左指针指向对应的下一层链表的节点,右指针指向当前链表的下一个节点,且树高为log(n),对于每一层需要比较的次数最多为k,则时间复杂度为O(k*log(n)),k为常数项,所以跳表查询时间复杂度为O(log(n))。因为需要额外的空间存储索引,是典型的以空间换时间,空间复杂度为O(n)。

接下来我们自己实现一个跳表:

节点数据结构定义:根据跳表结构,节点首先需要一个value存储当前节点值,需要一个next指针指向同一层的下一个节点,需要一个nodeValue指针指向下一层对应节点,但是这里为了插入删除方便,引入了一个prev指针,指向同一层的上一个节点。

class Node {
    //当前节点值
    private Integer value;
    //当前节点所属链表下一个节点
    private Node next;
    //当前节点所属链表上一个节点
    private Node prev;
    //当前节点指向的另一个索引链表/原始值链表节点
    private Node nodeValue;
    Node(Integer value) {
        this.value = value;
    }
}

初始化一个跳表:跳表的建立需要在数据有序的基础上,然后从下往上在下一层的基础上,间隔k生成当前层的节点,新生成的节点需要与当前层上一个节点连接起来,并且指向生成它的下一层节点。


private Node head ;

private List<Node> indexList;

private int level;


public void init() {
    //带头节点的链表,便于操作
    head = new Node(-1);
    head.next = head;
    indexList = new ArrayList<>();
    level = 0;
}

public void init(int k, int[] nums) {
    //初始化数据链表
    Node temp = head;
    for (int num : nums) {
        Node cur = new Node(num);
        cur.prev = temp;
        temp.next = cur;
        temp = temp.next;
    }
    //新节点保存(最底层)
    indexList.add(head);

    //循环生成索引结构,结束条件,当层仅一个元素
    temp = head.next;
    while (true) {
        //当前链表第几个元素
        int i = 0;
        //生成另一条链表长度
        int size = 0;
        Node indexNode = new Node(-1);
        indexNode.next = indexNode;
        Node indexNodeTemp = indexNode;
        while (null != temp) {
            //间隔k生成节点
            if (i % k == 0) {
                Node curNode = new Node(temp.value);
                curNode.nodeValue = temp;
                curNode.prev = indexNodeTemp;
                indexNodeTemp.next = curNode;
                indexNodeTemp = indexNodeTemp.next;
                ++ size;
            }
            ++ i;
            temp = temp.next;
        }
        indexList.add(indexNode);
        temp = indexNode.next;
        //当生成的索引链表仅1时不需要再继续生成
        if (size == 1) {
            break;
        }
    }
    level = indexList.size();
}

从跳表中查找元素:从最顶层索引链表开始查找,找到第一个大于当前节点的元素,则需要查找的元素在当前节点与之前节点之间,则从当前节点的上一个节点prev往下nodevalue继续进行查找,直到当前节点值与查找值相等,则直接返回当前节点,返回的节点可能是索引节点,也可能是原始数据节点,如果需要找到原始数据节点,则通过nodeValue继续往下找。


public boolean hasNum(int num) {
    Node result = this.findNum(num);
    return null != result;
}

public Node findNum(int num) {
    //跳表结构indexList是数据-》第一层索引-》第二层索引-》。。。。
    //1.直接匹配到
    //2.找到第一个大于当前元素的数,找前一个
    Node node = indexList.get(indexList.size() - 1).next;
    Node last = null;
    while (null != node) {
        if (node.value == num) {
            //已经找到元素
            return node;
        }
        if (node.value > num) {
            if (null == last) {
                //比最小值还小
                return null;
            }
            //找到了第一个大于num的索引node
            //到下一层去继续找
            node = last.nodeValue;
            last = null;
            continue;
        }
        last = node;
        node = null != node.next ? node.next : node.nodeValue;
    }
    return null;
}

删除节点:首先通过上面的查找方法找到目标节点,如果目标节点是索引值,则需要从当前索引层,层层往下删除包括原始数据链表,如果是原始数据值,则直接删除,暂不调整。


public boolean remove(int num) {
    Node node = this.findNum(num);
    if (null == node) {
        //不需要移除
        return false;
    }
    if (null == node.nodeValue) {
        //数据链表,可以直接移除
        //是否最后一个节点
        if (null == node.next) {
            node.prev.next = null;
            return true;
        }
        node.next.prev = node.prev;
        node.prev.next = node.next;
        return true;
    }
    //当前在索引上,自上而下删除索引及数据
    while (null != node) {
        Node cur = node.nodeValue;
        if (null == node.next) {
            node.prev.next = null;
        } else {
            node.next.prev = node.prev;
            node.prev.next = node.next;
        }
        node = cur;
    }
    return true;
}

新增节点:新增节点时候,如果不对索引进行调整,极端情况下,每次新增的节点都在之前第一层两个节点之间,当这之间的链表越变越长,时间复杂度直接退化为O(n),所以需要同时新增索引,维持跳表的高效性。但是我们如何新增,有一个方法就是,在新增节点时,随机选择k,即第k级索引,从1~k新增索引。


public void add(int num) {
    int k = this.generatorLevelk();
    //寻找插入点的过程和查找过程基本一致
    //顶级索引链表
    Node node = indexList.get(indexList.size() - 1).next;
    int index = 1;
    while (null != node) {
        //找到第一个node.value >= num的元素,在前面插入
        if (node.value >= num) {
            //已经找到,前插
            if (index >= k) {
                Node newNode = new Node(num);
                Node temp = node.prev;
                newNode.next = temp.next;
                temp.next.prev = newNode;
                newNode.prev = temp;
                temp.next = newNode;
            }
            //找的时候往后面找的,但是当前已经先于num了,下一次再往后面找,就出现问题
            if (null == node.prev.prev) {
                //第一个节点就符合条件
                node = node.nodeValue;
                continue;
            }
            node = node.prev.nodeValue;
            ++ index;
            continue;
        }

        //没有找到,但是当前已经是链表最后一个元素了
        if (null == node.next) {
            if (index >= k) {
                Node newNode = new Node(num);
                newNode.prev = node;
                node.next = newNode;
            }
            if (null == node.prev.prev) {
                //第一个节点就符合条件
                node = node.nodeValue;
                continue;
            }
            node = node.prev.nodeValue;
            ++ index;
            continue;
        }

        node = node.next;
    }

}

private int generatorLevelK() {
    Random random = new Random();
    return random.nextInt(level);
}

至此,我们实现了一个跳表的定义,初始化,查找,节点新增与删除。

到此这篇关于Java实现跳跃表的示例详解的文章就介绍到这了,更多相关Java跳跃表内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Java实现跳跃表的示例详解

本文链接: https://lsjlt.com/news/148732.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Java实现跳跃表的示例详解
    跳表全称叫做跳跃表,简称跳表,是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表。跳表在原有的有序列表上面增加多级索引,通过索引来实现快速查找。跳表不仅能提高搜索性能,同...
    99+
    2024-04-02
  • GO实现跳跃表的示例详解
    目录跳跃表介绍跳跃表的实现跳跃表的结构创建跳跃表跳跃表的插入和删除跳跃表的排名操作跳跃表的区间操作完整实现跳跃表介绍 跳跃表(skiplist)是一种有序的数据结构,它通过建立多层&...
    99+
    2022-12-19
    GoLang跳跃表 GO跳跃表
  • 详解Java中跳跃表的原理和实现
    目录一、跳跃表的引入二、算法分析1.时间复杂度2.空间复杂度三、跳跃表介绍四、跳跃表的实现1.数据结构定义2.查找3.插入4.删除五、实战1.代码2.测试结果一、跳跃表的引入 对有序...
    99+
    2022-12-27
    Java实现跳跃表 Java跳跃表原理 Java跳跃表
  • Java实现跳跃表(skiplist)的简单实例
    跳跃链表是一种随机化数据结构,基于并联的链表,其效率可比拟于二叉查找树(对于大多数操作需要O(log n)平均时间),并且对并发算法友好。基本上,跳跃列表是对有序的链表增加上附加的前进链接,增加是以随机化的方式进行的,所以在列表中的查找可以...
    99+
    2023-05-31
    java 跳跃表 skiplist
  • Java跳跃游戏实例真题解决思路详解
    目录变式题—跳跃游戏 I一、题目描述二、思路三、代码变式题—跳跃游戏 II一、题目描述二、思路三、代码变式题—跳跃游戏 I 一、题目描述 给定一个...
    99+
    2022-11-13
    Java跳跃游戏 Java跳跃游戏实例
  • Java利用跳跃表解决双重队列问题详解
    目录一 问题描述二 输入三 输出四 输入和输出样例五 分析和设计六 代码七 测试一 问题描述 银行的每个客户都有一个正整数标识 K,到银行请求服务时将收到一个正整数的优先级...
    99+
    2022-12-28
    Java跳跃表解决双重队列 Java跳跃表 Java 双重队列
  • Redis跳跃表的基本原理和实现
    目录一、概述二、跳跃表的实现2.1 跳跃表节点的zskiplisNode结构定义2.2 zskiplist结构的定义三、结束一、概述 跳跃表(skiplist)是一种有序数...
    99+
    2024-04-02
  • Java实现FutureTask的示例详解
    目录前言FutureTask自己实现FutureTask工具准备FutureTask设计与实现总结前言 在并发编程当中我们最常见的需求就是启动一个线程执行一个函数去完成我们的需求,而...
    99+
    2022-11-13
    Java 实现FutureTask Java FutureTask
  • python实现跳表SkipList的示例代码
    跳表 跳表,又叫做跳跃表、跳跃列表,在有序链表的基础上增加了“跳跃”的功能,由William Pugh于1990年发布,设计的初衷是为了取代平衡树(比如红黑树)。 Redis、LevelDB 都是著名的 Key-Va...
    99+
    2022-06-02
    python 跳表SkipList python 跳表
  • vue实现列表展示示例详解
    目录Vue 的CSS之deep语法::v-deepclassPrefix 前缀给元素绑定class总结Object.freeze关于Vue和ts的配合问题ISO8601和dayjs库...
    99+
    2024-04-02
  • Java实现无向图的示例详解
    目录基本概念图的定义无向图的定义无向图的 API无向图的实现方式邻接矩阵边的数组邻接表数组无向图的遍历深度优先搜索广度优先搜索后记基本概念 图的定义 一个图是由点集V={vi}&nb...
    99+
    2024-04-02
  • JavaWeb实现表单提交的示例详解
    目录register.htmlRegisterServlet.java修改web.xml,添加如下code重新配置服务器先点击左侧图标再点击Redeploy,重新部署Tomcat服务...
    99+
    2024-04-02
  • java实现Yaml转Json示例详解
    目录缘起调研过程1.0 入口点1.1 基本用法1.2 自定义类型解析1.3 实战1.3.1 从本地读配置文件1.3.2 从配置中心读配置文件缘起 年前,因为项目需要进行配置的优化和...
    99+
    2023-02-13
    java实现Yaml转Json Yaml转Json
  • Java实现并查集示例详解
    目录题目思路find实现join的实现整体代码 题目 题目背景 若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关...
    99+
    2024-04-02
  • Java利用SPI实现解耦的示例详解
    目录概述实现案例优势和不足概述 SPI的全称是服务提供接口,可以用其来启动框架的扩展和替换组件。 其本质是利用 接口实现+策略模式+配置文件来实现对实现类的动态加载。 在具体的使用中...
    99+
    2023-05-14
    Java SPI实现解耦 Java SPI解耦 Java 解耦
  • Java实现差分数组的示例详解
    目录前言应用场景Leetcode题目实战题目描述思路代码前言 昨天(2022-06-07)在做leetcode每日一题的时候,第一次看到了这个超级简单但是很实用的算法---差分数组,...
    99+
    2024-04-02
  • Java实现调用ElasticSearch API的示例详解
    目录java操作es有两种方式Elasticsearch-Rest-Client(官方,推荐)maven配置文件es配置类导包Spring Data ElasticSearch配置文...
    99+
    2023-03-02
    Java调用ElasticSearch API Java ElasticSearch API Java ElasticSearch
  • Java实现图片合成的示例详解
    目录场景环境搭建引入pom文件定义核心接口ImageService定义核心接口实现类ImageServiceImpl测试ImageController测试效果总结场景 前端有一个神器...
    99+
    2024-04-02
  • vue中详情跳转至列表页实现列表页缓存的示例分析
    小编给大家分享一下vue中详情跳转至列表页实现列表页缓存的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!提了一个需求,希...
    99+
    2024-04-02
  • java 相交链表的实现示例
    目录1.题目2.分析3.完整代码1.题目 相交链表:给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作