实现根据条件删除_常见数据结构的实现(一):跳跃表
知乎的小伙伴们好,这是我在知乎写的第一篇文章哈。我写这篇文章的目的主要是和大家分享一些想法,交流学习一下。
这系列的文章是分析常见数据结构的实现,包括跳跃表、二叉堆、前缀树、红黑树等等。。。数据结构这门课在学习与工作中都非常重要,所以我觉得有必要把自己的想法拿出来和大家分享交流,互相学习。
下面就开始正题吧!(第一次写文章,如果某些地方语言、思路表达不当或者有误,希望大家包容一下吧哈哈)
介绍
链表是一种基本的数据结构,而跳跃表是一种特殊的有序链表。
- 跳跃表是由多层有序链表组合而成的,最底一层的链表保存了所有的数据,每向上的一层链表依次保存了下一层链表的部分数据。
- 相邻的两层链表中元素相同的节点之间存在引用关系,一般是上层节点中存在一个指向下层节点的引用
- 跳跃表的目的在于提高了查询效率,同时也牺牲了一定的存储空间。
下面的一张图是我自己绘制的跳跃表,每层包含了头节点并且是单向链表:
分析
按照上面的图,所有节点包含以下内容:存储的元素,指向同一层中下一个节点的引用,指向下一层中对应节点的引用。
那么我们可以如下构建节点类和跳跃表类:
//跳跃表的实现
//元素有序且不重复
public class SkipList {private Node head;//顶层头节点 private int rate;//相邻两层元素个数的比例 private int level;//跳跃表层数 private int length;//底层节点个数 private int size;//所有层节点个数private final boolean order;//true表示正序,false表示逆序 private Random random;//随机数 private Stack<Node> stack;//保存查询时遍历的节点//节点类private static class Node {private Comparable comparable;private Node right;//同一层的右边节点private Node down;//下一层的对应节点public Node(Comparable comparable) {this.comparable = comparable;this.right = null;this.down = null;}}public SkipList(int rate, int level, boolean order) {this.rate = rate;this.level = level;this.length = 0;this.size = 0;this.order = order;this.random = new Random();this.stack = new Stack<Node>();this.head = new Node(null);//头节点的值默认为nullNode temp = head;for (int i = 1; i < level; i++) {temp.down = new Node(null);temp = temp.down;}}}
- rate表示相邻两层链表的元素数量之比,也就是下一层每添加多少个元素时,就向上一层继续添加一个元素。这个值可以由个人决定
- order表示有序链表保存元素的顺序,对于整型数据,从前向后,元素从小到大为true,对于字符型数据,从前向后,元素的字典序依次靠后为true,等等
- random用于添加操作时生成随机数的种子
- stack表示查询操作时保存查询路径上某些节点的栈,Stack类可以自己实现
SkipList类构造方法的最后几句代码表示初始化几个头节点,其中head指向最上层的头节点。我的实现中跳跃表保存的元素不重复,如果想要保存重复的元素,只需要在原来的基础上稍作修改即可
查找元素
添加、删除操作都依赖于查找操作,所以先介绍查找操作
查找操作是自顶向下、从左向右的,也就是先在最上一层链表中从左向右查找,然后依次向下层查找,直到最下一层的某个节点结束。具体的操作依赖于保存元素的顺序,假设保存的是整型数据,order为true,那么元素从前向后依次变大:
- 先在最上一层查找小于所指定元素的最右的节点,将节点入栈,然后转向下一层
- 在当前层继续向后查找满足上述条件的节点,同样将其入栈,向下一层继续查找,直到查找到最下一层满足条件的节点
从上面的分析中可以知道,stack中保存的是查找路径中每层最右边的节点,最底层的那个节点除外。
举个例子,在第一张图中,查找元素6时,查找路径为head、3、3、3、5、5,stack中保存的元素依次为3、3、5。查找元素10时,查找路径为head、3、3、7、7、7、8,stack中保存的元素依次为3、7、7。特殊情况下,头节点也会入栈
为什么这里设计成这样的查找路径,以及栈中保存查找路径上每层最右的节点?主要是在单链表的情况下,查找元素时可以统一元素存在和不存在两种情况,同时添加、删除元素时方便改变节点之间的引用关系。下面贴出查找操作的代码:
//查询元素,自顶向下//正序时,返回底层【小于】给定值的最大的节点,包含头节点//逆序时,返回底层【大于】给定值的最小的节点,包含头节点private Node search(Comparable comparable) {stack.clear();Node temp = head;//从顶层开始while (true) {while (temp.right != null) {if (order && temp.right.comparable.compareTo(comparable) >= 0)//正序时查找当前层【小于】给定值的最大的节点break;if (!order && temp.right.comparable.compareTo(comparable) <= 0)//逆序时查找当前层【大于】给定值的最小的节点break;temp = temp.right;}if (temp.down == null)//找到底层的节点break;stack.push(temp);//stack保存遍历路径中每一层最右边的节点,除底层外temp = temp.down;//转到下一层}return temp;}
如果理解了查找操作的过程,那么上面的代码就很容易看懂了。查找操作返回的是最下一层满足那个条件的节点(有可能是头节点)。
添加元素
在我的实现中,跳跃表不保存重复的元素,所以只有当所指定元素不存在时,才执行添加操作。
添加操作是自底向上的,并且根据指定的rate按照一定的概率向上层添加节点。添加时需要维护同层节点之间的关系,同时也要维护当前节点与下一层对应节点的关系。这里只需要注意的一点是,什么条件下才能向上层继续添加?只有当随机数满足条件并且当前层不是最上一层时,才能继续添加。
//添加元素//若元素已存在,则返回,保证无重复元素public void insert(Comparable comparable) {Node temp = search(comparable);if (temp.right != null && temp.right.comparable.compareTo(comparable) == 0)//元素已存在return;Node node = new Node(comparable);Node other;//根据随机数,自底向上添加每层的新节点while (true) {node.right = temp.right;temp.right = node;//当前层添加完毕size++;if (random.nextInt(rate) != 0 || stack.isEmpty())break;//若随机数为0且还未到顶层,则向上层添加元素temp = stack.pop();other = node;node = new Node(comparable);node.down = other;}length++;return;}
再强调一下,查找操作返回的是最下一层满足条件的节点。注意下,while循环里面向上层添加单个节点的过程实际上分解到了两次循环中,先维护上下层节点的关系,再维护同层节点的关系。
删除元素
当所指定元素存在时,删除操作需要删除所有层中的对应元素。如果某一层中不存在指定的元素,那么上面的所有层中肯定也不会存在,因此可以直接跳出循环,操作结束。
//删除元素//若元素不存在,则返回,否则删除所有层中包含的元素public void delete(Comparable comparable) {Node temp = search(comparable);if (temp.right == null || temp.right.comparable.compareTo(comparable) != 0)//元素不存在return;while (true) {if (temp.right == null || temp.right.comparable.compareTo(comparable) != 0) //当前层的元素不存在break;//从底层开始,依次删除每层的元素temp.right = temp.right.right;size--;if (stack.isEmpty())//到达顶层break;temp = stack.pop();//转到上一层}length--;return;}
后记
从上面的讨论中可以看到,跳跃表的查找、添加、删除操作其实并不难理解,这个数据结构比较简单。当然这篇文章是我的个人理解,欢迎感兴趣的读者一起来交流,提出建议。后面我会介绍其他一些常用数据结构的实现,希望大家继续关注哦~
实现根据条件删除_常见数据结构的实现(一):跳跃表相关推荐
- springboot和vue data数据为空_常见数据结构的 Python 实现(建议收藏)
数据结构作为计算机基础的必修内容,也是很多大型互联网企业面试的必考题.可想而知,它在计算机领域的重要性. 然而很多计算机专业的同学,都仅仅是了解数据结构的相关理论,却无法用代码实现各种数据结构. 今日 ...
- java 二叉树 红黑树_常见数据结构(二)-树(二叉树,红黑树,B树)
常见数据结构(二)-树(二叉树,红黑树,B树) 标签: algorithms [TOC] 本文介绍数据结构中几种常见的树:二分查找树,2-3树,红黑树,B树 写在前面 本文所有图片均截图自course ...
- c语言中的code6,第6讲_嵌入式C语言_常见数据结构及算法
嵌入式C语言编程--常见数据结构及算法 GDAIB Data Structure and Arithmetic 结构.联合.枚举\r用结构构成链表\r单向链表\r双向链表\r循环双向链表\rC语言中的 ...
- c语言数据结构常考算法,第6讲嵌入式C语言_常见数据结构及算法..docx
/复习结构.联合.枚举 /复习结构.联合.枚举Page 3 GDAIB Embedded C Programming 嵌入式c语言编程--常见数据结构及算法 Data Structure and Ar ...
- 数据结构(字典,跳跃表)、使用场景(计数器、缓存、查找表、消息队列、会话缓存、分布式锁)、Redis 与 Memcached、 键的过期时间、数据淘汰策略、持久化(RDB、AOF)
1. 数据结构 1.1 字典 dictht 是一个散列表结构,使用拉链法保存哈希冲突的 dictEntry /* This is our hash table structure. Every dic ...
- python与js通用的数据结构_常见数据结构和Javascript实现总结
做前端的同学不少都是自学成才或者半路出家,计算机基础的知识比较薄弱,尤其是数据结构和算法这块,所以今天整理了一下常见的数据结构和对应的Javascript的实现,希望能帮助大家完善这方面的知识体系. ...
- 删除第一个_学习数据结构--第二章:线性表(顺序存储、插入、删除)
第二章:线性表(顺序表示) 1.线性表的定义和基本操作 线性表是具有相同数据类型的n(n≥0)个数据元素的有限序列 线性表中第一个元素称为表头元素;最后一个元素称为表尾元素. 除第一个元素外,每个元素 ...
- 实现根据条件删除_强大的定位空值法,1秒删除所有不想要的数据
私信回复关键词[CSV],获取CSV工具,帮你批量转换上百个Excel文件格式! 前几天在微信后台看到这样一个问题: 这个表格里,每隔一分钟就有一条温度与湿度等数据的记录. 但我只想每隔五分钟留下一条 ...
- spring elasticsearch 按条件删除_实战:项目数据源转为Elasticsearch
原本项目是基于MYSQL的,现因需求将其转换为MYSQL+Elasticsearch,MYSQL的ORM使用的是Spring Data Jpa,Mybatis的转换与其类似,有人看再更 先看原项目 原 ...
最新文章
- Kalilinux安装openvas
- Java中如何读写cookie (二)
- 洛谷 - P1111 - 修复公路 - 并查集
- TCP/IP原理 (一)
- QT的QRandomGenerator类的使用
- 146. LRU Cache
- LeetCode 744. Find Smallest Letter Greater Than Target (时间复杂度O(n))
- 使用cisco 2500路由器实现ADSL接入
- 程序员找 Bug 福音!微软全新开源查找修复 Bug 工具——Project OneFuzz
- 如何检查手机上的 App 是不是正版?
- 用Diff和Patch工具维护源码
- B/S VS C/S
- mysql2000清除挂起工具,安装SQL提示挂起操作解决方法
- 计算机图形学的未来前景,计算机图形学的发展前景
- 2019腾讯广告算法大赛思路(转自简书)有所改动(标蓝)
- linux命令行计算器 bc命令用法
- 欢迎来到whai的博客
- Android利用SpannableStringBuilder设置TextView中部分文字的颜色...
- Pytorch optimizer.step() 和loss.backward()和scheduler.step()的关系与区别
- IDEA社区版搭建Tomcat服务器并创建web项目
热门文章
- npm run 脚本背后的事情
- Angular 路由的一个问题,以及解决方案
- 关于 Angular 服务实例作用域的问题 - 使用组件限定服务提供者的作用域
- 什么是SAP Spartacus schematics
- SAP Spartacus里product数据请求的HTTP url是在哪里维护的
- 第一次做开源项目,和做SAP标准开发不同的感受
- SAP Spartacus路由参数的默认配置
- SAP SRM ABAP Webdynpro和CFCA usb key集成的一个原型开发
- OPA 1 - testsuite.opa.html
- How is navigation target url request handled by backend