我的编程空间,编程开发者的网络收藏夹
学习永远不晚

详解Java中跳跃表的原理和实现

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

详解Java中跳跃表的原理和实现

一、跳跃表的引入

对有序顺序表可以采用二分查找,查找的时间复杂度为O (logn),插入、删除的时间复杂度为 O(n )。但是对有序链表不可以采用二分查找,查找、插入和删除的时间复杂度均为O (n)。

有序链表如下图所示,若查找 8,则必须从第 1 个节点开始,依次比较 8 次才能查找成功。

如何利用链表的有序性来提高查找效率?如何在一个有序链表中进行二分查找?若增加一级索引,把奇数位序作为索引,则如下图所示,若查找 8,则可以先从索引进行比较,依次比较 1、3、5、7、9,8 比 7 大但比 9 小,7 向下一层,继续向后比较,比较 6 次即可查找成功。

若再增加一级索引,把索引层的奇数位序作为索引,则如下图所示,若查找 7,则可以先从索引开始比较,依次 1、5、9,7 比 5 大但比 9 小,5 向下一层,继续向后比较,比较 4 次即可查找成功。

在增加两级索引后,若查找 5,则比较两次即可查找成功;若查找比 5 大的数,则以 5 为界向后查找;若查找比 5 小的数,则以 5 为界向前查找。这就是一个可进行二分查找的有序链表。

二、算法分析

若有 n 个元素,则增加 h 级索引后的数据结构如下图所示。

1.时间复杂度

底层包含所有元素(n个),即 2^(h +1)=n ,索引层数 h=logn-1。搜索时,首先在顶层索引中进行查找,然后二分搜索,最多从顶层搜索到底层,最多 O(logn) 层,因此查找的时间复杂度为 O(logn)。

2.空间复杂度

增加索引需要一些辅助空间,那么索引一共有多少个节点呢?从上图中可以看出,每层索引的节点之和都为 2+2^2 +…+2^h =2^(h +1)-2=n-2,因此空间复杂度为 O(n )。实际上,索引节点并不是原节点的复制,只是附加了一些指针建立索引。以上正是跳跃表的实现原理。

三、跳跃表介绍

跳跃表(Skip list)是有序链表的扩展,简称跳表,它在原有的有序链表上增加了多级索引,通过索引来实现快速查找,实质上是一种可以进行二分查找的有序链表。

实际上,跳跃表并不是简单地通过奇偶次序建立索引的,而是通过随机技术实现的,因此也可以说它是一种随机化的数据结构。假如跳跃表每一层的晋升概率都是 1/2,则最理想的索引就是在原始链表中每隔一个元素抽取一个元素作为一级索引。其实在原始链表中随机选择 n/2 个元素作为一级索引也可以,因为随机选择的元素相对均匀,对查找效率来讲影响不大。当原始链表中元素数量足够大且抽取足够随机时,得到的索引是均匀的。因此随机选择 n/2 个元素作为一级索引,随机选择 n/4 个元素作为二级索引,随机选择 n/8 个元素作为三级索引,以此类推,一直到顶层索引。我们可以通过索引来提升跳跃表的查找效率。

跳跃表不仅可以提高搜索性能,还可以提高插入和删除操作的性能。平衡二叉查找树在进行插入、删除操作后需要多次调整平衡,而跳跃表完全依靠随机技术,其性能和平衡二叉查找树不相上下,但是原理非常简单。跳跃表是一种性能比较优秀的动态数据结构,Redis 中的有序集合 Sorted Set 和 LevelDB 中的 MemTable 都是采用跳跃表实现的。

四、跳跃表的实现

1.数据结构定义

在每个节点都可以设置向右、向下指针,当然,也可以附加向左、向上指针,构建四联表。通过四联表可以快速地在四个方向访问前驱和后继。在此仅设置向右指针,在每个节点都定义一个后继指针数组,通过层次控制向下访问。

2.查找

在跳跃表中查找元素 x ,需要从最上层索引开始逐层查找,算法步骤如下。

(1)从最上层 Sh 的头节点开始。

(2)假设当前位置为 p ,p 的后继节点的值为 y ,若 x=y,则查找成功;若 x>y ,则 p 向后移一个位置,继续查找;若 x<y ,则 p 向下移动一个位置,继续查找。

(3)若到达底层还要向下移动,则查找失败。

例如,跳跃表如下图所示,在表中查找元素 36,则先从顶层的头节点开始,比 20 大,向后为空,p 向下移动到第 2 层;比下一个元素 50 小,p 向下移动到第 1 层;比下一个元素 30 大,p 向右移动;比下一个元素 50 小,p 向下移动到第 0 层;与下一个元素 36 比较,查找成功。

3.插入

在跳跃表中插入一个元素时,相当于在某一个位置插入一个高度为 level 的列。插入的位置可以通过查找确定,而插入列的高度可以采用随机化决策确定。

随机化获取插入元素的层次:

① 初始时 lay=0,可设定晋升概率P为 0.5 或 0.25。

② 利用随机函数产生 0~1的随机数 r。

④ 若 r 小于 P 且 lay 小于最大层次,则 lay+1;否则返回 lay,作为新插入元素的高度。

在跳跃表中插入元素的算法步骤如下。

(1)查找插入位置,在查找过程中用 updata[i] 记录经过的每一层的最大节点位置。

(2)采用随机化策略得到插入层次 lay。

(3)创建新节点,将高度为 lay 的列插入 updata[i] 之后。

例如,跳跃表如下图所示,在表中插入元素 32。首先在跳跃表中查找 32,然后确定插入位置。在查找过程中用 updata[i] 记录经过的每一层的最大节点位置;假设随机化得到的层次为 2,则 i 为 0~2,将新节点插入 updata[i] 之后。

4.删除

在跳跃表中删除一个元素,相当于删除这个元素所在的列。

算法步骤:

(1)查找该元素,在查找过程中用 updata[i] 记录经过的每一层的最大节点位置,实际上是待删除节点在每一层的前一个元素的位置。若查找失败,则退出。

(2)利用 updata[i] 将该元素整列删除。

(3)若有多余空链,则删除空链。

例如,跳跃表如下图所示,在表中删除元素 20。首先在跳跃表中查找 20,在查找过程中用 updata[i] 记录经过的每一层的最大节点位置;然后利用 updata[i] 将每层的 20 节点删除。

删除 20 所在的列后,最上层的链为空链,则删除空链,跳跃表的层次减 1。

五、实战

1.代码

package com.platform;
 
import java.util.Scanner;
 
public class SkipList {
    private static int INF = 0x7fffffff;
    private static float P = 0.5f;
    private static int MAX_LEVEL = 16;
 
    static class Node {
        int val;
        Node forward[] = new Node[MAX_LEVEL];
    }
 
 
    Node head = new Node();
    Node updata[] = new Node[MAX_LEVEL];
 
    public SkipList() {
        for (int i = 0; i < updata.length; i++) {
            updata[i] = new Node();
        }
    }
 
    void Init() {
        level = 0;
        head = new Node();
        for (int i = 0; i < MAX_LEVEL; i++)
            head.forward[i] = null;
        head.val = -INF;
    }
 
    // 随机产生插入元素高度
    static int RandomLevel() {
        int lay = 0;
        while ((float) Math.random() < P && lay < MAX_LEVEL - 1)
            lay++;
        return lay;
    }
 
    Node Find(int val) {//查找最接近val的元素
        Node p = head;
        for (int i = level; i >= 0; i--) {
            while (p.forward[i] != null && p.forward[i].val < val)
                p = p.forward[i];
            updata[i] = p;//记录搜索过程中各级走过的最大节点位置
        }
        return p;
    }
 
    void Insert(int val) {
        Node p, s;
        int lay = RandomLevel();
        System.out.printf("lay=%d\n", lay);
        if (lay > level) //要插入的层 > 现有层数
            level = lay;
        p = Find(val); //查询
        s = new Node();//创建一个新节点
        s.val = val;
        for (int i = 0; i < MAX_LEVEL; i++)
            s.forward[i] = null;
        for (int i = 0; i <= lay; i++) {//插入操作
            s.forward[i] = updata[i].forward[i];
            updata[i].forward[i] = s;
        }
    }
 
    void Delete(int val) {
        Node p = Find(val);
        if (p.forward[0] != null && p.forward[0].val == val) {
            System.out.printf("%d\n", p.forward[0].val);
            for (int i = level; i >= 0; i--) { // 删除操作
                if (updata[i].forward[i] != null && updata[i].forward[i].val == val)
                    updata[i].forward[i] = updata[i].forward[i].forward[i];
            }
            while (level > 0 && head.forward[level] == null) // 删除空链
                level--;
        }
    }
 
    void print(int i) { // 遍历
        Node p = head.forward[i];
        if (p == null) return;
        while (p != null) {
            System.out.printf("%d  ", p.val);
            p = p.forward[i];
        }
        System.out.printf("\n");
    }
 
    void printall() { // 遍历所有层
        for (int i = level; i >= 0; i--) {
            System.out.printf("level %d:  ", i);
            print(i);
        }
    }
 
    void show() {
        System.out.printf("Please select:\n");
        System.out.printf("-----------------------------\n");
        System.out.printf("     1:  插入\n");
        System.out.printf("     2:  删除\n");
        System.out.printf("     3:  查找\n");
        System.out.printf("     4:  遍历\n");
        System.out.printf("     5:  遍历所有层\n");
        System.out.printf("     0:  退出\n");
        System.out.printf("-----------------------------\n");
    }
 
    int level;
 
    public static void main(String[] args) {
        Scanner sn = new Scanner(System.in);
        SkipList skipList = new SkipList();
        skipList.Init();
        int n, x;
        skipList.show();
        while (true){
            n = sn.nextInt();
            switch (n) {
                case 1:
                    x = sn.nextInt();
                    skipList.Insert(x);
                    break;
                case 2:
                    x = sn.nextInt();
                    skipList.Delete(x);
                    break;
                case 3:
                    x = sn.nextInt();
                    Node p;
                    p = skipList.Find(x);
                    if (p.forward[0] != null && p.forward[0].val == x) {
                        System.out.printf("find success!\n");
                    } else {
                        System.out.printf("find fail!\n");
                    }
 
 
                    break;
                case 4:
                    skipList.print(0);
                    break;
                case 5:
                    skipList.printall();
                    break;
            }
            skipList.show();
        }
    }
}

2.测试结果

Please select:
-----------------------------
     1:  插入
     2:  删除
     3:  查找
     4:  遍历
     5:  遍历所有层
     0:  退出
-----------------------------
1
1
lay=0
Please select:
-----------------------------
     1:  插入
     2:  删除
     3:  查找
     4:  遍历
     5:  遍历所有层
     0:  退出
-----------------------------
5
level 0:  1  
Please select:
-----------------------------
     1:  插入
     2:  删除
     3:  查找
     4:  遍历
     5:  遍历所有层
     0:  退出
-----------------------------
1
4
lay=5
Please select:
-----------------------------
     1:  插入
     2:  删除
     3:  查找
     4:  遍历
     5:  遍历所有层
     0:  退出
-----------------------------
5
level 5:  4  
level 4:  4  
level 3:  4  
level 2:  4  
level 1:  4  
level 0:  1  4  
Please select:
-----------------------------
     1:  插入
     2:  删除
     3:  查找
     4:  遍历
     5:  遍历所有层
     0:  退出
-----------------------------
1
7
lay=1
Please select:
-----------------------------
     1:  插入
     2:  删除
     3:  查找
     4:  遍历
     5:  遍历所有层
     0:  退出
-----------------------------
5
level 5:  4  
level 4:  4  
level 3:  4  
level 2:  4  
level 1:  4  7  
level 0:  1  4  7  
Please select:
----------------------------
     1:  插入
     2:  删除
     3:  查找
     4:  遍历
     5:  遍历所有层
     0:  退出
-----------------------------
1
2
lay=0
Please select:
-----------------------------
     1:  插入
     2:  删除
     3:  查找
     4:  遍历
     5:  遍历所有层
     0:  退出
-----------------------------
5
level 5:  4  
level 4:  4  
level 3:  4  
level 2:  4  
level 1:  4  7  
level 0:  1  2  4  7  
Please select:
-----------------------------
     1:  插入
     2:  删除
     3:  查找
     4:  遍历
     5:  遍历所有层
     0:  退出
-----------------------------
3
3
find fail!
Please select:
-----------------------------
     1:  插入
     2:  删除
     3:  查找
     4:  遍历
     5:  遍历所有层
     0:  退出
-----------------------------
3
2
find success!
Please select:
-----------------------------
     1:  插入
     2:  删除
     3:  查找
     4:  遍历
     5:  遍历所有层
     0:  退出
-----------------------------
2
2
2
Please select:
-----------------------------
     1:  插入
     2:  删除
     3:  查找
     4:  遍历
     5:  遍历所有层
     0:  退出
-----------------------------
5
level 5:  4  
level 4:  4  
level 3:  4  
level 2:  4  
level 1:  4  7  
level 0:  1  4  7  
Please select:
-----------------------------
     1:  插入
     2:  删除
     3:  查找
     4:  遍历
     5:  遍历所有层
     0:  退出
-----------------------------

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

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

详解Java中跳跃表的原理和实现

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

详解Java中跳跃表的原理和实现

跳跃表(Skiplist)是有序链表的扩展,简称跳表,它在原有的有序链表上增加了多级索引,通过索引来实现快速查找,实质上是一种可以进行二分查找的有序链表。本文主要为大家介绍了跳跃表的原理和实现,需要的可以参考一下
2022-12-27

GO实现跳跃表的示例详解

跳表全称叫做跳跃表,简称跳表,是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表。本文将利用GO语言编写一个跳表,需要的可以参考一下
2022-12-19

Java实现跳跃表(skiplist)的简单实例

跳跃链表是一种随机化数据结构,基于并联的链表,其效率可比拟于二叉查找树(对于大多数操作需要O(log n)平均时间),并且对并发算法友好。基本上,跳跃列表是对有序的链表增加上附加的前进链接,增加是以随机化的方式进行的,所以在列表中的查找可以
2023-05-31

java中多态概念、实现原理详解

一.什么是多态?1.多态的定义指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用)2.多态的作用消除类型之间的耦合关系3.多态的说明近代网络小说泛滥,我们可以用它来举一个例子
2023-05-31

Java中ThreadLocal的用法和原理详解

这篇文章主要为大家详细介绍了Java中ThreadLocal的用法和原理,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的可以了解一下
2023-05-15

详解HTTPS 的原理和 NodeJS 的实现

基本原理HTTP协议采用明文传输数据,当涉及敏感信息的传送时,极有可能会受到窃听或者中间人的攻击。HTTPS是HTTP与SSL/TLS的组合,即使用加密通讯以及网络服务器的身份鉴定来进行信息的安全传输。其核心有二:使用证书对服务器及请求端的
2022-06-04

C++中function的实现原理详解

类模版std::function是一种通用、多态的函数封装。function的实例可以对任何可以调用的目标实体进行存储、复制、和调用操作。本文主要聊聊它的实现原理,需要的可以参考一下
2022-12-09

详解Android中Handler的实现原理

在 Android 中,只有主线程才能操作 UI,但是主线程不能进行耗时操作,否则会阻塞线程,产生 ANR 异常,所以常常把耗时操作放到其它子线程进行。如果在子线程中需要更新 UI,一般是通过 Handler 发送消息,主线程接受消息并且进
2022-06-06

Java线程的停止实现原理详解

这篇文章主要介绍了Java线程的停止实现原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
2023-01-28

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录