跳表的由来及Java实现详解

跳表的由来及Java实现详解

1. 什么是跳表?

跳表(Skip List)是一种基于随机化的数据结构,用来实现有序数据的动态插入、删除和查找操作。跳表其实就是一个多层的单向链表,每一层的节点都是前一层节点的子节点,且每个节点都有概率生成更高层的后续节点。由于跳表适用于数据元素有序且动态插入、删除的情况,因此在一些高性能并发库的实现中有广泛的应用。

2. 跳表的发明历史

跳表的发明者为William Pugh,其于1990年在ACM上发表了名为Skipped List的文章,实现了一种以O(logN)时间复杂度平均的方法在跳表中进行查找、插入和删除操作,并将跳表的理论性质发扬光大地应用于同步算法的设计中。从此跳表开始受到广泛的研究和应用。

3. 跳表的Java实现

具体的Java代码实现可以参考如下的示例:

3.1 定义跳表节点类

class SkipListNode<T> {
    public T value;
    public SkipListNode<T>[] next;

    public SkipListNode(T value, int level) {
        this.value = value;
        this.next = new SkipListNode[level];
    }
}

SkipListNode类表示跳表的节点,包含了节点的值和跳表的前后指针,用于构建跳表的多层结构。

3.2 跳表类的定义

public class SkipList<T> {
    private static final double DEFAULT_PROBABILITY = 0.5; // 节点加入新的一层的概率

    private SkipListNode<T> head;
    private int levelCount;
    private int size;
    private Random random;
    private double probability;

    public SkipList() {
        this.head = new SkipListNode<T>(null, 0);
        this.levelCount = 1;
        this.size = 0;
        this.random = new Random();
        this.probability = DEFAULT_PROBABILITY;
    }

    // 查找节点
    public SkipListNode<T> find(T value) {
        SkipListNode<T> p = head;
        for (int i = levelCount - 1; i >= 0; i--) { // 从前往后找到每层的前序节点
            while (p.next[i] != null && p.next[i].value.compareTo(value) < 0) {
                p = p.next[i];
            }
        }
        if (p.next[0] != null && p.next[0].value.equals(value)) { // 如果最后一层的节点是目标节点则返回
            return p.next[0];
        }
        return null;
    }

    // 插入节点
    public void insert(T value) {
        int level = randomLevel(); // 随机生成插入节点的层数
        SkipListNode<T> newNode = new SkipListNode<T>(value, level);
        SkipListNode<T>[] update = new SkipListNode[level];
        SkipListNode<T> p = head;

        for (int i = level - 1; i >= 0; i--) { // 从前往后遍历每层,找到待插入节点的前序节点,并记录在update数组中
            while (p.next[i] != null && p.next[i].value.compareTo(value) < 0) {
                p = p.next[i];
            }
            update[i] = p;
        }

        for (int i = 0; i < level; i++) { // 从下往上构建新节点的指针链接
            newNode.next[i] = update[i].next[i];
            update[i].next[i] = newNode;
        }
        size++;
    }

    // 删除节点
    public void delete(T value) {
        SkipListNode<T>[] update = new SkipListNode[levelCount];
        SkipListNode<T> p = head;
        for (int i = levelCount - 1; i >= 0; i--) { // 从上到下遍历每层,找到待删除节点的前序节点,并记录在update数组中
            while (p.next[i] != null && p.next[i].value.compareTo(value) < 0) {
                p = p.next[i];
            }
            update[i] = p;
        }
        if (p.next[0] != null && p.next[0].value.equals(value)) { // 如果找到了目标节点,则开始删除操作
            for (int i = 0; i < levelCount; i++) { // 从上到下逐层删除
                if (update[i].next[i] != null && update[i].next[i].value.equals(value)) {
                    update[i].next[i] = update[i].next[i].next[i];
                }
            }
            size--;
        }
    }

    // 随机生成新节点的层数
    private int randomLevel() {
        int level = 1;
        while (random.nextDouble() < probability && level < 32) {
            level++;
        }
        return level;
    }
}

SkipList类表示跳表,包含了跳表的各种操作方法。find方法用于查找特定的值;insert方法用于插入新节点;delete方法用于删除节点。其中,randomLevel方法用于随机生成新节点的层数。

3.3 示例一:查找元素

public static void example1() {
    SkipList<Integer> list = new SkipList<>();
    for (int i = 0; i < 10; i++) { // 插入元素
        list.insert(i);
    }
    SkipListNode<Integer> node = list.find(5); // 查找元素
    System.out.println(node.value);
}

此示例中,首先构建了一个包含10个元素的跳表。然后通过调用find方法查找元素5所在的节点,并打印出节点的值。

3.4 示例二:删除元素

public static void example2() {
    SkipList<Integer> list = new SkipList<>();
    for (int i = 0; i < 10; i++) { // 插入元素
        list.insert(i);
    }
    list.delete(5); // 删除元素
    SkipListNode<Integer> node = list.find(5); // 查找元素
    System.out.println(node);
}

此示例中,首先构建了一个包含10个元素的跳表。然后通过调用delete方法删除元素5。最后通过调用find方法查找元素5所在的节点,并打印出结果null。

4. 总结

跳表是一种基于随机化的有序数据结构,适用于动态插入、删除和查找场景,具有O(logN)时间复杂度平均的特性。Java中可以通过定义SkipListNode和SkipList类来实现跳表的基本操作,为多种场景提供了高效的数据结构支持。

阅读剩余 70%

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:跳表的由来及Java实现详解 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • Python中关于面向对象中继承的详细讲解

    当我们创建一个新类时,如果这个新类与我们之前定义过的某个类非常相似,我们可以使用继承来避免重复编写相同的代码,从而实现代码重用的目的。 什么是继承? 继承是指一个类可以继承另一个类的特征和行为,被继承的类被称为父类(或基类、超类),继承这些类的类被称为子类(或派生类)。 子类可以访问父类中定义的属性和方法,并且可以在自己的类中添加新的属性和方法。 继承的语法…

    other 2023年6月26日
    00
  • php md5下16位和32位的实现代码

    PHP MD5算法 MD5(Message Digest Algorithm 5)是一种常用的哈希算法,用于将任意长度的数据转换为固定长度的哈希值。在PHP中,可以使用内置的md5()函数来计算MD5哈希值。 16位MD5哈希值 要获取16位的MD5哈希值,可以通过截取32位MD5哈希值的一部分来实现。下面是一个示例代码: <?php function…

    other 2023年7月28日
    00
  • 如何在vite初始化项目中安装scss以及scss的使用

    在Vite初始化项目中安装SCSS以及SCSS的使用攻略 安装SCSS 首先,确保你已经安装了Node.js和npm。你可以在终端中运行以下命令来检查它们的版本: node -v npm -v 使用Vite初始化一个新项目。在终端中运行以下命令: npm init vite@latest my-project –template blank 进入项目目录:…

    other 2023年8月9日
    00
  • js数组常用最重要的方法

    当我们用JavaScript编写程序时,数组是我们常用的数据类型之一。学习JavaScript数组的常用方法能够帮助我们更加高效地处理数据。下面,我将详细讲解JavaScript数组常用最重要的方法,包括创建数组、添加和删除元素、访问和修改元素、数组遍历以及数组的一些常见操作。 创建数组 我们可以通过以下方式来创建一个JavaScript数组: // 创建一…

    other 2023年6月25日
    00
  • C 语言环境设置详细讲解

    C 语言环境设置详细讲解 设置开发环境 在进行 C 语言开发之前,需要安装相应的开发环境,包括编译器和集成开发环境。以下是安装步骤: 安装编译器 Windows 系统可以安装 GCC 编译器。安装步骤如下: a. 下载 MinGW 安装程序,选择 mingw-get-setup.exe。 b. 运行安装程序,按照提示安装 MinGW。 c. 安装完成后,在系…

    other 2023年6月26日
    00
  • 详解Spring-boot中读取config配置文件的两种方式

    下面是详解Spring-boot中读取config配置文件的两种方式的完整攻略。 一、介绍 在Spring-boot中,有两种主要的方式来读取配置文件: 使用注解@Value读取文件中的属性值; 使用@ConfigurationProperties注解将属性值绑定为Java类的字段。 这两种方式都可以读取文件中的属性值,只是实现的方式不同。 下面将逐一介绍这…

    other 2023年6月25日
    00
  • win7提示1分钟后重启怎么回事?win7系统1分钟自动重启解决方法

    Win7提示1分钟后重启怎么回事? 当你在电脑使用Win7系统时,某些情况下,你可能会看到一个弹窗提示框,上面写着“系统将在1分钟后自动关机重启”,这时候你肯定会觉得十分苦恼以及不知道该如何解决。下面,我们将讲解怎么回事以及如何解决这个问题。 什么是Win7提示1分钟后重启的问题? Win7提示1分钟后重启是一个非常常见的Windows系统故障。当你的电脑系…

    other 2023年6月27日
    00
  • guava本地缓存

    以下是关于Guava本地缓存的完整攻略,包含两个示例。 Guava本地缓存 Guava是Google开发的一个Java库,提供了许多实用的工具类和数据结构。其中,Guava本地缓存是一个非常实用的工具,可以帮助我们应用程序中缓存数据,提高应用程序的性能。以下是使用Guava本地缓存的详细攻略。 1. 添加依赖 在使用Guava本地缓存之前,我们需要在项目中添…

    other 2023年5月9日
    00
合作推广
合作推广
分享本页
返回顶部