知乎的小伙伴们好,这是我在知乎写的第一篇文章哈。我写这篇文章的目的主要是和大家分享一些想法,交流学习一下。

这系列的文章是分析常见数据结构的实现,包括跳跃表、二叉堆、前缀树、红黑树等等。。。数据结构这门课在学习与工作中都非常重要,所以我觉得有必要把自己的想法拿出来和大家分享交流,互相学习。

下面就开始正题吧!(第一次写文章,如果某些地方语言、思路表达不当或者有误,希望大家包容一下吧哈哈)

介绍

链表是一种基本的数据结构,而跳跃表是一种特殊的有序链表。

  • 跳跃表是由多层有序链表组合而成的,最底一层的链表保存了所有的数据,每向上的一层链表依次保存了下一层链表的部分数据。
  • 相邻的两层链表中元素相同的节点之间存在引用关系,一般是上层节点中存在一个指向下层节点的引用
  • 跳跃表的目的在于提高了查询效率,同时也牺牲了一定的存储空间。

下面的一张图是我自己绘制的跳跃表,每层包含了头节点并且是单向链表:

skip_list.png

分析

按照上面的图,所有节点包含以下内容:存储的元素,指向同一层中下一个节点的引用,指向下一层中对应节点的引用。

那么我们可以如下构建节点类和跳跃表类:

//跳跃表的实现
//元素有序且不重复
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;}

后记

从上面的讨论中可以看到,跳跃表的查找、添加、删除操作其实并不难理解,这个数据结构比较简单。当然这篇文章是我的个人理解,欢迎感兴趣的读者一起来交流,提出建议。后面我会介绍其他一些常用数据结构的实现,希望大家继续关注哦~

欣赏美可以使人愉悦~~~

实现根据条件删除_常见数据结构的实现(一):跳跃表相关推荐

  1. springboot和vue data数据为空_常见数据结构的 Python 实现(建议收藏)

    数据结构作为计算机基础的必修内容,也是很多大型互联网企业面试的必考题.可想而知,它在计算机领域的重要性. 然而很多计算机专业的同学,都仅仅是了解数据结构的相关理论,却无法用代码实现各种数据结构. 今日 ...

  2. java 二叉树 红黑树_常见数据结构(二)-树(二叉树,红黑树,B树)

    常见数据结构(二)-树(二叉树,红黑树,B树) 标签: algorithms [TOC] 本文介绍数据结构中几种常见的树:二分查找树,2-3树,红黑树,B树 写在前面 本文所有图片均截图自course ...

  3. c语言中的code6,第6讲_嵌入式C语言_常见数据结构及算法

    嵌入式C语言编程--常见数据结构及算法 GDAIB Data Structure and Arithmetic 结构.联合.枚举\r用结构构成链表\r单向链表\r双向链表\r循环双向链表\rC语言中的 ...

  4. c语言数据结构常考算法,第6讲嵌入式C语言_常见数据结构及算法..docx

    /复习结构.联合.枚举 /复习结构.联合.枚举Page 3 GDAIB Embedded C Programming 嵌入式c语言编程--常见数据结构及算法 Data Structure and Ar ...

  5. 数据结构(字典,跳跃表)、使用场景(计数器、缓存、查找表、消息队列、会话缓存、分布式锁)、Redis 与 Memcached、 键的过期时间、数据淘汰策略、持久化(RDB、AOF)

    1. 数据结构 1.1 字典 dictht 是一个散列表结构,使用拉链法保存哈希冲突的 dictEntry /* This is our hash table structure. Every dic ...

  6. python与js通用的数据结构_常见数据结构和Javascript实现总结

    做前端的同学不少都是自学成才或者半路出家,计算机基础的知识比较薄弱,尤其是数据结构和算法这块,所以今天整理了一下常见的数据结构和对应的Javascript的实现,希望能帮助大家完善这方面的知识体系. ...

  7. 删除第一个_学习数据结构--第二章:线性表(顺序存储、插入、删除)

    第二章:线性表(顺序表示) 1.线性表的定义和基本操作 线性表是具有相同数据类型的n(n≥0)个数据元素的有限序列 线性表中第一个元素称为表头元素;最后一个元素称为表尾元素. 除第一个元素外,每个元素 ...

  8. 实现根据条件删除_强大的定位空值法,1秒删除所有不想要的数据

    私信回复关键词[CSV],获取CSV工具,帮你批量转换上百个Excel文件格式! 前几天在微信后台看到这样一个问题: 这个表格里,每隔一分钟就有一条温度与湿度等数据的记录. 但我只想每隔五分钟留下一条 ...

  9. spring elasticsearch 按条件删除_实战:项目数据源转为Elasticsearch

    原本项目是基于MYSQL的,现因需求将其转换为MYSQL+Elasticsearch,MYSQL的ORM使用的是Spring Data Jpa,Mybatis的转换与其类似,有人看再更 先看原项目 原 ...

最新文章

  1. Kalilinux安装openvas
  2. Java中如何读写cookie (二)
  3. 洛谷 - P1111 - 修复公路 - 并查集
  4. TCP/IP原理 (一)
  5. QT的QRandomGenerator类的使用
  6. 146. LRU Cache
  7. LeetCode 744. Find Smallest Letter Greater Than Target (时间复杂度O(n))
  8. 使用cisco 2500路由器实现ADSL接入
  9. 程序员找 Bug 福音!微软全新开源查找修复 Bug 工具——Project OneFuzz
  10. 如何检查手机上的 App 是不是正版?
  11. 用Diff和Patch工具维护源码
  12. B/S VS C/S
  13. mysql2000清除挂起工具,安装SQL提示挂起操作解决方法
  14. 计算机图形学的未来前景,计算机图形学的发展前景
  15. 2019腾讯广告算法大赛思路(转自简书)有所改动(标蓝)
  16. linux命令行计算器 bc命令用法
  17. 欢迎来到whai的博客
  18. Android利用SpannableStringBuilder设置TextView中部分文字的颜色...
  19. Pytorch optimizer.step() 和loss.backward()和scheduler.step()的关系与区别
  20. IDEA社区版搭建Tomcat服务器并创建web项目

热门文章

  1. npm run 脚本背后的事情
  2. Angular 路由的一个问题,以及解决方案
  3. 关于 Angular 服务实例作用域的问题 - 使用组件限定服务提供者的作用域
  4. 什么是SAP Spartacus schematics
  5. SAP Spartacus里product数据请求的HTTP url是在哪里维护的
  6. 第一次做开源项目,和做SAP标准开发不同的感受
  7. SAP Spartacus路由参数的默认配置
  8. SAP SRM ABAP Webdynpro和CFCA usb key集成的一个原型开发
  9. OPA 1 - testsuite.opa.html
  10. How is navigation target url request handled by backend