【跳表】

2023-05-16

跳表原理

跳变是一种高效的搜索结构,是对链表的一种优化,如下图所示,以下图片均摘自博客,原始的链表结构如下图,实现查找、删除和插入复杂度都为O(n),因为需要遍历链表

在这里插入图片描述
而跳表的思想如下,即多加一些索引,高层的索引跳跃连接,可以更快地抵达要查找的目标节点,查找时先从最高层索引开始,高速前进,当下一个节点的值大于要查找的值时就进入下一层,相当于减速,直到最底层找到目标节点

在这里插入图片描述

时间复杂度

相对于原始链表,优势在于高层索引可以快速跨越,能实现 O ( l o g 2 n ) O(log_2n) O(log2n)级别的查找、删除和插入,计算如下,其实是一种近似的计算方法,默认在每一层只查找一次,然后就进入下一层,则查找的次数就是跳表的高度h,但是一般情况下每一层肯定不止找一次,因此最小的查找次数就是跳表的高度h,关键是求得h,通过找规律很容易得出:
一级索引的节点为 n / 2 n/2 n/2,二级索引的节点数为 n / 4 n/4 n/4,则第k级索引的节点数为 n / 2 k n/2^k n/2k,最高级索引H通常只有两个节点,即头尾,因此满足 2 = n / 2 H 2=n/2^H 2=n/2H,解得 H = l o g 2 n − 1 H=log_2n-1 H=log2n1,再加上原始的链表得到跳表的层数为 h = H + 1 = l o g 2 n h=H+1=log_2n h=H+1=log2n,因此时间复杂度为 O ( l o g 2 n ) O(log_2n) O(log2n)

Java实现

Java实现如下,相对于红黑树等二分查找结构实现更简单

import java.util.Random;

class SkipList {
	private static final int MAX_LEVEL = 16;
	private int levelCount = 1;
	private Node head = new Node();
	private Random random = new Random();

	class Node {
		private int data;
		// forwards: 存储该节点在各个level索引的下一个节点
		private Node[] forwards = new Node[MAX_LEVEL];
		private int maxLevel;

		public String toString() {
			StringBuilder sb = new StringBuilder();
			sb.append("{data: ");
			sb.append(data);
			sb.append("; level: ");
			sb.append(maxLevel);
			sb.append(" }");
			return sb.toString();
		}
	}

	public Node find(int value) {
		// 查找的时候从最顶层开始,因为最顶层跨度大,可以很快接近要查找的值
		Node p = head;
		for (int i = levelCount - 1; i >= 0; i--) {
			while (p.forwards[i] != null && p.forwards[i].data < value)
				// 在这一层一直找,直到找到最后一个小于value的节点,然后跳出进入下一层查找
				p = p.forwards[i];
		}
		if (p.forwards[0] != null && p.forwards[0].data == value)
			// p.forwards[0]:p的第0层的下一个节点,如果是value 那就找到了,否则没找到
			return p.forwards[0];
		return null;
	}

	public void insert(int value) {
		Node p = head;
		int level = randomLevel();
		Node node = new Node();
		node.data = value;
		node.maxLevel = level;
		Node update[] = new Node[level]; // 更新level级以下的所有索引
		for (int i = level - 1; i >= 0; i--) {
			while (p.forwards[i] != null && p.forwards[i].data < value)
				p = p.forwards[i];
			// 找到这一层里面小于value的最后一个节点,然后保存在update中,下次在这里插入新节点
			update[i] = p;
		}
		for (int i = 0; i < level; i++) {
			// 这里实际上就是链表的插入,很简单,相当于每一层都插入一个
			node.forwards[i] = update[i].forwards[i];
			update[i].forwards[i] = node;
		}
		if (levelCount < level)
			levelCount = level; // 当前最大层数
	}

	public void delete(int value) {
		Node[] deleteNode = new Node[MAX_LEVEL];
		Node p = head;
		for (int i = levelCount - 1; i >= 0; i--) {
			// 先找到它在这一层的最后一个比要删除节点小的节点
			while (p.forwards[i] != null && p.forwards[i].data < value)
				p = p.forwards[i];
			deleteNode[i] = p;
		}
		if (p.forwards[0] != null && p.forwards[0].data == value)
			// p.forwards[0] != null && p.forwards[0].data == value 首先保证这个节点在跳表中
			for (int i = levelCount - 1; i >= 0; i--)
				if (deleteNode[i] != null && deleteNode[i].forwards[i].data == value)
					// 相当于把中间要删除的那个节点的连接断开,跨到它后面那个节点
					deleteNode[i].forwards[i] = deleteNode[i].forwards[i].forwards[i];
	}

	public void printAll() {
		Node p = head;
		while (p.forwards[0] != null) {
			System.out.print(p.forwards[0] + " ");
			p = p.forwards[0];
		}
		System.out.println();
	}

	private int randomLevel() {
		int level = 0;
		for (int i = 0; i < MAX_LEVEL; i++) {
			if (random.nextInt() % 2 == 1) {
				level++;
			}
		}
		return level;
	}

}

public class main {
	public static void main(String[] args) {
		SkipList list = new SkipList();
		list.insert(1);
		list.insert(4);
		list.insert(3);
		list.insert(5);
		list.printAll();
	}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【跳表】 的相关文章

  • 【堆】建堆、插入、删除、堆排序

    参考 堆就是利用数组来实现二叉树 xff0c 可用于构建优先队列 堆排序 TopK问题等 可分为 xff1a 最大堆 xff1a 父节点的值比其子节点大最小堆 xff1a 父节点的值比其子节点小 堆的根节点存放了最小 xff08 或最大 x
  • 【RDD编程】cache持久化使用场景

    Spark中RDD采用惰性求值的机制 xff0c 每次遇到action操作都会触发一次从头开始执行的计算 xff0c 在某些场景下这会使得程序性能大幅度降低 例如下面例子 xff0c 在rdd13 count 时将触发一次从rdd1开始到r
  • 【Java】自带sort库使用

    Arrays sort arr span class token keyword public span span class token keyword class span span class token class name mai
  • 如何使UDEV规则有效

    转 victor 64 X301A1 ls etc udev rules d 70 persistent cd rules 70 persistent net rules README 然后 xff1a victor 64 X301A1 s
  • 【堆】剑指 Offer 40. 最小的k个数

    输入整数数组 arr xff0c 找出其中最小的 k 个数 例如 xff0c 输入4 5 1 6 2 7 3 8这8个数字 xff0c 则最小的4个数字是1 2 3 4 示例 1 xff1a 输入 xff1a arr 61 3 2 1 k
  • 【堆】703. 数据流中的第K大元素

    设计一个找到数据流中第K大元素的类 xff08 class xff09 注意是排序后的第K大元素 xff0c 不是第K个不同的元素 你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器 xff0c 它包含数据
  • 【Queue】简单使用

    java中LinkedList实现了Queue接口 xff0c 可以当作队列使用 添加元素 xff1a offer或add方法 xff0c add方法在失败的时候会抛出异常 不推荐 删除元素 xff1a remove和poll方法都是从队列
  • 【树】剑指 Offer 55 - I. 二叉树的深度

    题目 输入一棵二叉树的根节点 xff0c 求该树的深度 从根节点到叶节点依次经过的节点 xff08 含根 叶节点 xff09 形成树的一条路径 xff0c 最长路径的长度为树的深度 例如 xff1a 给定二叉树 span class tok
  • 【树】剑指 Offer 28. 对称的二叉树

    题目 请实现一个函数 xff0c 用来判断一棵二叉树是不是对称的 如果一棵二叉树和它的镜像一样 xff0c 那么它是对称的 例如 xff0c 二叉树 1 2 2 3 4 4 3 是对称的 span class token number 1
  • 【图】1042. 不邻接植花

    题目 有 N 个花园 xff0c 按从 1 到 N 标记 在每个花园中 xff0c 你打算种下四种花之一 paths i 61 x y 描述了花园 x 到花园 y 的双向路径 另外 xff0c 没有花园有 3 条以上的路径可以进入或者离开
  • 【LinkedList】基本操作、图的邻接表

    基本操作 创建 LinkedList span class token generics function span class token punctuation lt span Integer span class token punc
  • 【Python】配置文件configparser

    使用configparser模块读取模型参数 xff0c 设置config ini文件内容如下 xff0c train 和 savepath 分别为两个session span class token punctuation span tr
  • 【Python】生成随机字符串

    参考 span class token keyword import span random span class token keyword def span span class token function random str sp
  • 【动态规划】64. 最小路径和

    题目 给定一个包含非负整数的 m x n 网格 xff0c 请找出一条从左上角到右下角的路径 xff0c 使得路径上的数字总和为最小 说明 xff1a 每次只能向下或者向右移动一步 示例 输入 1 3 1 1 5 1 4 2 1 输出 7
  • 树莓派无法安装pyqt5与pandas

    问题描述 使用pip3 install安装一些包 xff0c 例如pyqt5 pandas无法成功 sudo pip3 install pandas sudo pip3 install pyqt5 无法安装 解决方案 xff1a 安装pan
  • 【Java】二维数组初始化

    带值初始化 span class token keyword int span a span class token punctuation span span class token punctuation span span class
  • 【图】1162. 地图分析(多源BFS)

    题目 你现在手里有一份大小为 N x N 的 地图 xff08 网格 xff09 grid xff0c 上面的每个 区域 xff08 单元格 xff09 都用 0 和 1 标记好了 其中 0 代表海洋 xff0c 1 代表陆地 xff0c
  • 【tensorflow】数据增强

    使用tf image对图片进行数据增强 读入图片 span class token keyword from span PIL span class token keyword import span Image span class to
  • 【HashMap】使用自定义类作为key

    需要重写hashCode 和equals 方法才能实现自定义键在HashMap中的查找 span class token keyword class span span class token class name Pos span spa
  • 【图】1267. 统计参与通信的服务器

    题目 这里有一幅服务器分布图 xff0c 服务器的位置标识在 m n 的整数矩阵网格 grid 中 xff0c 1 表示单元格上有服务器 xff0c 0 表示没有 如果两台服务器位于同一行或者同一列 xff0c 我们就认为它们之间可以进行通

随机推荐

  • 【并查集】Java实现

    并查集理解 并查集的数据结构实现一般是数组 xff0c 通过数组来指示各个元素之间的父子关系 xff0c 通常初始化为 1 xff0c 若最终该位置的值大于0 xff0c 则表示该位置是一个孩子 xff0c 其父亲为节点的值 并查集的两个重
  • 【并查集】721. 账户合并

    题目 给定一个列表 accounts xff0c 每个元素 accounts i 是一个字符串列表 xff0c 其中第一个元素 accounts i 0 是 名称 name xff0c 其余元素是 emails 表示该帐户的邮箱地址 现在
  • 【并查集】面试题 17.07. 婴儿名字

    题目 每年 xff0c 政府都会公布一万个最常见的婴儿名字和它们出现的频率 xff0c 也就是同名婴儿的数量 有些名字有多种拼法 xff0c 例如 xff0c John 和 Jon 本质上是相同的名字 xff0c 但被当成了两个名字公布出来
  • 【Java】字符串比较compareTo

    根据字典序比较两个字符串的大小 xff0c 使用compareTo方法 xff0c 如下 xff0c 如果字符串str1和str2相等则res 61 0 xff0c 若str1字典序小于str2则res lt 0 xff0c 否则res g
  • 【Java】String indexOf substring截取字符串

    使用indexOf char c 方法获取字符串中第一次出现字符c的下标 xff0c 例如 span class token keyword public span span class token keyword class span s
  • 树莓派3B+环境搭建

    转载 xff1a https blog csdn net zhangjun62 article details 80517176 我的树莓派3b 43 没有买HDMI 屏 xff0c 利用网线与电脑主机相连操纵树莓派 如果买回来接上电 xf
  • 【Scala】创建整型数组

    var res span class token operator 61 span new ArrayBuffer span class token punctuation span Int span class token punctua
  • 【RDD编程】map和mapPartitions

    map和mapPartitions map针对RDD中的每一个元素调用一次函数 xff0c 而mapPartitions针对RDD中每个Partition调用一次函数 xff0c 假设RDD有N个元素 xff0c 有M个分区 xff0c 那
  • 【Spark入门项目】词频统计

    项目要求 要求统计txt英文文件中每个单词出现的次数 txt文件内随机拷贝英文内容 xff0c 如下 The scientists re analysed a sample collected by NASA astronauts duri
  • 【jieba】中文分词

    span class token keyword import span jieba words span class token operator 61 span jieba span class token punctuation sp
  • 【Python】读取中文

    fn span class token operator 61 span span class token builtin open span span class token punctuation span path span clas
  • 【WordCloud】生成词云

    generate from frequencies xff1a 从频率字典中生成词云 该方法传入统计好的词频字典 xff0c 例如 39 Python 39 5 39 Hadoop 39 10 39 Spark 39 20 39 大数据 3
  • 【Spark入门项目】统计男女生身高的平均值、最大、最小值

    项目要求 分别统计男女生身高的平均值 最大 最小值 xff0c 数据格式为 xff08 ID xff0c sex xff0c height xff09 xff0c 如下 xff1a 1 M 174 2 F 165 3 M 180 4 M 1
  • 【Spark入门项目】关键词统计

    项目描述 统计txt文件中出现频率前10的关键词 xff0c 内如如下 实现流程 初始化spark配置通过textFile方法读取txt文件通过flatMap将RDD中的每一个元素调用split方法分词 xff0c split中使用jieb
  • 【二叉搜索树】面试题 17.12. BiNode

    题目 二叉树数据结构TreeNode可用来表示单向链表 xff08 其中left置空 xff0c right为下一个链表节点 xff09 实现一个方法 xff0c 把二叉搜索树转换为单向链表 xff0c 要求依然符合二叉搜索树的性质 xff
  • 【二叉搜索树】1305. 两棵二叉搜索树中的所有元素(非递归中序遍历)

    题目 给你 root1 和 root2 这两棵二叉搜索树 请你返回一个列表 xff0c 其中包含 两棵树 中的所有整数并按 升序 排序 示例 1 xff1a 输入 xff1a root1 span class token operator
  • 51单片机的RFID门禁系统

    一 硬件方案 本RFID系统设计可分为硬件部分和软件部分 硬件部分以MFRC522射频识别模块为核心 xff0c 结合主控模块STC89C52设计系统的外围硬件电路 xff0c 实现对射频卡的控制与MCU之间的互通 软件部分采用C语言进行系
  • 【win10 Kafka基本操作】启动、创建topic、发送信息

    1 启动Zookeeper Kafka依赖于zookeeper xff0c 因此需要先启动zookeeper xff0c 使用kafka内置的zookeeper xff0c cd到kafka的bin windows目录下 xff0c 运行如
  • java.lang.IllegalArgumentException: Unsupported class file major version 58

    使用scala编写代码 xff0c 程序报错 java lang IllegalArgumentException Unsupported class file major version 58 改错误为jdk版本和scala不兼容 xff
  • 【跳表】

    跳表原理 跳变是一种高效的搜索结构 xff0c 是对链表的一种优化 xff0c 如下图所示 xff0c 以下图片均摘自博客 xff0c 原始的链表结构如下图 xff0c 实现查找 删除和插入复杂度都为O n xff0c 因为需要遍历链表 而