跳跃表

news/2024/12/23 14:42:30/文章来源:https://www.cnblogs.com/Yee-Q/p/18401654

概述

跳跃表(SkipList)是链表加多级索引组成的数据结构。链表的数据结构的查询复条度是 O(N)。为了提高查询效率,可以在链表上加多级索引来实现快速查询。跳跃表不仅能提高搜索性能。也能提高插入和删除操作的性能。索引的层数也叫作跳跃表的高度


查找

在跳跃表的结构中会首先从顶层开始查找,当顶层不存在时向下一层查找,重复此查找过程直到跳跃到原始链表。如图所示,在 [1,3,4,10,1,20] 的链表中查找 10,首先从二级索引中查找,由于 1 和 4 都比 10 小,因此接着在一级索引查找,由于 10 大于 4 小于 11,因此接着向下查找,原始链表中 4 的下一个节点 10 便是需要查找的数据


插入

首先按照查找流程找到待插入元素的前驱和后继,然后按照随机算法生成一个高度值 height,最后将待插入的节点按照高度值生成一个垂直节点(这个节点的层数正好等于高度值),并将其插人跳跃表的多条链表中。如果高度值 heigh 大于插入前跳跃表的高度,那么跳跃表的高度被提升为 height,同时需要更新头节点和尾节点的指针指向。如果 height 小于或等于跳跃表的高度,那么需要更新待插入元素的前驱和后继的指针指向。在 [1,3,4.10,11,20] 的跳跃表中插入 18 的过程如图所示


删除

在删除节点时首先需要找到待删除的节点在每一层的前驱和后继,接着将其前驱节点的后继替换为待删除的节点的后继,删除 4 的过程如图所示


代码实现

import java.util.Random;
import java.util.Stack;class SkipNode<T> {int key;T value;SkipNode right,down;//左右上下四个方向的指针public SkipNode (int key,T value) {this.key=key;this.value=value;}}public class SkipList <T> {SkipNode headNode;//头节点,入口int highLevel;//层数Random random;// 用于投掷硬币final int MAX_LEVEL = 32;//最大的层SkipList(){random=new Random();headNode=new SkipNode(Integer.MIN_VALUE,null);highLevel=0;}public SkipNode search(int key) {SkipNode team=headNode;while (team!=null) {if(team.key==key){return  team;}else if(team.right==null) { //右侧没有了,只能下降team=team.down;}else if(team.right.key>key) { //需要下降去寻找team=team.down;}else { //右侧比较小向右team=team.right;}}return null;}public void delete(int key) { //删除不需要考虑层数SkipNode team=headNode;while (team!=null) {if (team.right == null) { //右侧没有了,说明这一层找到,没有只能下降team=team.down;}else if(team.right.key==key) { //找到节点,右侧即为待删除节点team.right=team.right.right;//删除右侧节点team=team.down;//向下继续查找删除}else if(team.right.key>key) { //右侧已经不可能了,向下team=team.down;}else { //节点还在右侧team=team.right;}}}public void add(SkipNode node) {int key=node.key;SkipNode findNode=search(key);if(findNode!=null) { //如果存在这个key的节点findNode.value=node.value;return;}Stack<SkipNode>stack=new Stack<SkipNode>();//存储向下的节点,这些节点可能在右侧插入节点SkipNode team=headNode;//查找待插入的节点   找到最底层的哪个节点。while (team!=null) {//进行查找操作if(team.right==null) { //右侧没有了,只能下降stack.add(team);//将曾经向下的节点记录一下team=team.down;}else if(team.right.key>key) { //需要下降去寻找stack.add(team);//将曾经向下的节点记录一下team=team.down;}else { //向右team=team.right;}}int level=1;//当前层数,从第一层添加(第一层必须添加,先添加再判断)SkipNode downNode=null;//保持前驱节点(即down的指向,初始为null)while (!stack.isEmpty()) {//在该层插入nodeteam=stack.pop();//抛出待插入的左侧节点SkipNode nodeTeam=new SkipNode(node.key, node.value);//节点需要重新创建nodeTeam.down=downNode;//处理竖方向downNode=nodeTeam;//标记新的节点下次使用if(team.right==null) {//右侧为null 说明插入在末尾team.right=nodeTeam;}//水平方向处理else {//右侧还有节点,插入在两者之间nodeTeam.right=team.right;team.right=nodeTeam;}//考虑是否需要向上if(level>MAX_LEVEL)//已经到达最高级的节点啦break;double num=random.nextDouble();//[0-1]随机数if(num>0.5)//运气不好结束break;level++;if(level>highLevel) { //比当前最大高度要高但是依然在允许范围内 需要改变head节点highLevel=level;//需要创建一个新的节点SkipNode highHeadNode=new SkipNode(Integer.MIN_VALUE, null);highHeadNode.down=headNode;headNode=highHeadNode;//改变headstack.add(headNode);//下次抛出head}}}public void printList() {SkipNode teamNode=headNode;int index=1;SkipNode last=teamNode;while (last.down!=null){last=last.down;}while (teamNode!=null) {SkipNode enumNode=teamNode.right;SkipNode enumLast=last.right;System.out.printf("%-8s","head->");while (enumLast!=null&&enumNode!=null) {if(enumLast.key==enumNode.key) {System.out.printf("%-5s",enumLast.key+"->");enumLast=enumLast.right;enumNode=enumNode.right;}else {enumLast=enumLast.right;System.out.printf("%-5s","");}}teamNode=teamNode.down;index++;System.out.println();}}
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker 镜像的分层概念

来更深入地理解镜像的概念40.镜像的分层概念 来更深入地理解镜像的概念 ‍ 镜像的分层 镜像,是一种轻量级、可执行的独立软件包,它包含运行某个软件所需的所有内容,我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时需要的库、环境变量和配置文件等…

prometheus学习笔记之kube-state-metrics

一、kube-state-metrics简介Kube-state-metrics:通过监听 API Server 生成有关资源对象的状态指标,比如 Deployment、Node、Pod,需要注意的是 kube-state-metrics 只是简单的提供一个 metrics 数据, 并不会存储这些指标数据, 所以我 们可以使用 Prometheus 来抓取这些数据然…

事务发件箱模式在 .NET 云原生开发中的应用(基于Aspire)

原文:Transactional Outbox in .NET Cloud Native Development via Aspire 作者:Oleksii Nikiforov总览 这篇文章提供了使用 Aspire、DotNetCore.CAP、Azure Service Bus、Azure SQL、Bicep 和 azd 实现 Outbox 模式的示例。源代码: https://github.com/NikiforovAll/cap-as…

Css 斜线生成案例_Css 斜线/对角线整理

一、Css 斜线,块斜线,对角线 块的宽度高度任意支持<!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><tit…

mac升级node到指定版本

node版本升级到稳定版18.16.1 (1)node -v(2)npm cache clean -f 在使用npm cache clear --force清除缓存的时候,报npm WARN using --force Recommended protections disabled的错误,有可能是镜相源过期的问题换为npm cache verify(3) sudo n stable // 把当前系统的 Node 更…

HashMap深入讲解

HashMap是Java中最常用的集合类框架,也是Java语言中非常典型的数据结构, 而HashSet和HashMap者在Java里有着相同的实现,前者仅仅是对后者做了一层包装,也就是说HashSet里面有一个HashMap(适配器模式)。因此了解HashMap源码也就了解HashSet了 介绍Key的存储方式是基于哈希表…

如何实现mysql高可用

1.机器资源耗尽 2.单点故障 3.认为操作 4.网络单点故障解决方案? 1.搭建mysql主从集群(双主,一主多从,多主多从) 2. 可以用MyCat, ShardingJdbc实现A节点同步到B节点流程?1. 从库通过IO线程, 连接到主库,并且向主库要对应的bin log文件 2. 主库通过dump线程获取binlog文…

基于基尼指数构建分类决策树[算法+示例]

0 前言本文主要讲述使用基尼指数构建二叉决策树的算法,并给出例题一步步解析,帮助读者理解。 本文所使用的数据集:贷款.CSV。 读者需要具备的知识:基尼指数计算。1 基于基尼指数的分类树构建算法选择最优特征进行分裂: 对于决策树的每个节点,遍历数据集中的所有特征。对于…

Node.js版本管理工具之NVM

目录一、NVM介绍二、NVM的下载安装1、NVM下载2、卸载旧版Node.js3、安装三、NVM配置及使用1、设置nvm镜像源2、安装Node.js3、卸载Node.js4、使用或切换Node.js版本5、设置全局安装路径和缓存路径四、常用命令一、NVM介绍 在工作中,不同的项目可能需要不同NodeJS版本,所以维护…

Javaweb-DQL-分组查询

select sex,avg(math) from stu group by sex;-- 1 select sex,avg(math) as 数学平均分,count() as 人数 from stu group by sex;-- 2 select sex,avg(math) as 数学平均分,count() as 人数 from stu where math>=70 group by sex;-- 3 select sex,avg(math) as 数学平均分…

AP3215 8-150V 外围简单 宽输入 电压降压BUCK 恒压恒流驱动器 POE、电动车、扭扭车、电瓶车、车充方案

产品描述 AP3215是 一系列外围电路简洁的宽输入电压降压BUCK 恒压恒流驱动器 ,适用于8- 150V 输入电压范围 的DC-DC 降压应用。 AP3215输出电压通过 FB 管脚设置 ,输出电流通过 CS 电阻设置 ,外围简洁 , 具备高效率 ,低功耗 ,低纹波 , 优异 的线性调整率和负载调整率等优…

HTML 转 PDF API 接口

HTML 转 PDF API 接口 网络工具 / 文件处理 支持网页转 PDF 高效生成 PDF / 提供永久链接。1. 产品功能超高性能转换效率; 支持将传递的 HTML 转换为 PDF,支持转换 HTML 中的 CSS 格式; 支持传递网站 URL,直接转换页面成对应的 PDF 文件; 转换后的 PDF 提供永久存储文件地…