SortSet

有序的Set,其实在Java中TreeSet是SortSet的唯一实现类,内部通过TreeMap实现的;而TreeMap是通过红黑树实现的;而在Redis中是通过跳表实现的;

SkipList

跳表,思想类似平衡二叉树,但又不一样;下面摘了一个介绍:

skiplist数据结构简介(摘自:https://www.cnblogs.com/Elliott-Su-Faith-change-our-life/p/7545940.html)

skiplist本质上也是一种查找结构,用于解决算法中的查找问题(Searching),即根据给定的key,快速查到它所在的位置(或者对应的value)。

我们在《Redis内部数据结构详解》系列的第一篇中介绍dict的时候,曾经讨论过:一般查找问题的解法分为两个大类:一个是基于各种平衡树,一个是基于哈希表。但skiplist却比较特殊,它没法归属到这两大类里面。

这种数据结构是由William Pugh发明的,最早出现于他在1990年发表的论文《Skip Lists: A Probabilistic Alternative to Balanced Trees》。对细节感兴趣的同学可以下载论文原文来阅读。

skiplist,顾名思义,首先它是一个list。实际上,它是在有序链表的基础上发展起来的。

我们先来看一个有序链表,如下图(最左侧的灰色节点表示一个空的头结点):

在这样一个链表中,如果我们要查找某个数据,那么需要从头开始逐个进行比较,直到找到包含数据的那个节点,或者找到第一个比给定数据大的节点为止(没找到)。也就是说,时间复杂度为O(n)。同样,当我们要插入新数据的时候,也要经历同样的查找过程,从而确定插入位置。

假如我们每相邻两个节点增加一个指针,让指针指向下下个节点,如下图:

这样所有新增加的指针连成了一个新的链表,但它包含的节点个数只有原来的一半(上图中是7, 19, 26)。现在当我们想查找数据的时候,可以先沿着这个新链表进行查找。当碰到比待查数据大的节点时,再回到原来的链表中进行查找。比如,我们想查找23,查找的路径是沿着下图中标红的指针所指向的方向进行的:

23首先和7比较,再和19比较,比它们都大,继续向后比较。

但23和26比较的时候,比26要小,因此回到下面的链表(原链表),与22比较。

23比22要大,沿下面的指针继续向后和26比较。23比26小,说明待查数据23在原链表中不存在,而且它的插入位置应该在22和26之间。

在这个查找过程中,由于新增加的指针,我们不再需要与链表中每个节点逐个进行比较了。需要比较的节点数大概只有原来的一半。

利用同样的方式,我们可以在上层新产生的链表上,继续为每相邻的两个节点增加一个指针,从而产生第三层链表。如下图:

在这个新的三层链表结构上,如果我们还是查找23,那么沿着最上层链表首先要比较的是19,发现23比19大,接下来我们就知道只需要到19的后面去继续查找,从而一下子跳过了19前面的所有节点。可以想象,当链表足够长的时候,这种多层链表的查找方式能让我们跳过很多下层节点,大大加快查找的速度。

skiplist正是受这种多层链表的想法的启发而设计出来的。实际上,按照上面生成链表的方式,上面每一层链表的节点个数,是下面一层的节点个数的一半,这样查找过程就非常类似于一个二分查找,使得查找的时间复杂度可以降低到O(log n)。但是,这种方法在插入数据的时候有很大的问题。新插入一个节点之后,就会打乱上下相邻两层链表上节点个数严格的2:1的对应关系。如果要维持这种对应关系,就必须把新插入的节点后面的所有节点(也包括新插入的节点)重新进行调整,这会让时间复杂度重新蜕化成O(n)。删除数据也有同样的问题。

skiplist为了避免这一问题,它不要求上下相邻两层链表之间的节点个数有严格的对应关系,而是为每个节点随机出一个层数(level)。比如,一个节点随机出的层数是3,那么就把它链入到第1层到第3层这三层链表中。为了表达清楚,下图展示了如何通过一步步的插入操作从而形成一个skiplist的过程(点击看大图):

从上面skiplist的创建和插入过程可以看出,每一个节点的层数(level)是随机出来的,而且新插入一个节点不会影响其它节点的层数。因此,插入操作只需要修改插入节点前后的指针,而不需要对很多节点都进行调整。这就降低了插入操作的复杂度。实际上,这是skiplist的一个很重要的特性,这让它在插入性能上明显优于平衡树的方案。这在后面我们还会提到。

根据上图中的skiplist结构,我们很容易理解这种数据结构的名字的由来。skiplist,翻译成中文,可以翻译成“跳表”或“跳跃表”,指的就是除了最下面第1层链表之外,它会产生若干层稀疏的链表,这些链表里面的指针故意跳过了一些节点(而且越高层的链表跳过的节点越多)。这就使得我们在查找数据的时候能够先在高层的链表中进行查找,然后逐层降低,最终降到第1层链表来精确地确定数据位置。在这个过程中,我们跳过了一些节点,从而也就加快了查找速度。

刚刚创建的这个skiplist总共包含4层链表,现在假设我们在它里面依然查找23,下图给出了查找路径:

需要注意的是,前面演示的各个节点的插入过程,实际上在插入之前也要先经历一个类似的查找过程,在确定插入位置后,再完成插入操作。

至此,skiplist的查找和插入操作,我们已经很清楚了。而删除操作与插入操作类似,我们也很容易想象出来。这些操作我们也应该能很容易地用代码实现出来。

当然,实际应用中的skiplist每个节点应该包含key和value两部分。前面的描述中我们没有具体区分key和value,但实际上列表中是按照key进行排序的,查找过程也是根据key在比较。

红黑树:

这个介绍就多了,总结一下,一个自平衡的二叉查找树。

java treeset 红黑树_【数据结构】红黑树与跳表-(SortSet)-(TreeMap)-(TreeSet)相关推荐

  1. rsa算法c语言实现_数据结构与算法之线性表-顺序表实现(C语言版本)

    原文托管在Github: https://github.com/shellhub/blog/issues/52 数据结构与算法之线性表-顺序表实现(C语言版本) 前言 数据结构与算法是一个程序员必备的 ...

  2. 数据结构和算法之——跳表

    之前我们知道,二分查找依赖数组的随机访问,所以只能用数组来实现.如果数据存储在链表中,就真的没法用二分查找了吗?而实际上,我们只需要对链表稍加改造,就可以实现类似"二分"的查找算法 ...

  3. 数据结构与算法学习--跳表

    跳表 Skip List是一种随机化的数据结构,基于并联的链表,其效率可比拟于二叉查找树(对于大多数操作需要O(log n)平均时间).基本上,跳跃列表是对有序的链表增加上附加的前进链接,增加是以随机 ...

  4. 二叉查找树 平衡二叉查找树 红黑树 b树 b+树 链表 跳表 链表

    https://www.cnblogs.com/mojxtang/p/10122587.html二叉树的新增遍历查找 转载于:https://www.cnblogs.com/wangjing666/p ...

  5. java单链表输出_数据结构基础------1.线性表之单链表的创建与输出方法(Java版)...

    基础知识: 线性表(linear list),是其组成元素间具有线性关系的一种线性结构. 线性表有 ①顺序存储结构(sequential storage structure) 顺序存储结构可以简单的理 ...

  6. java链表的用法_数据结构(java语言描述)链表的使用

    1.定义node类,实现Node结点的构造函数(空结点和指定数值的结点),基本的获取结点的指针域,数据域,设置指针域,数据域. package class2; public class Node { ...

  7. java冒泡测试代码_数据结构与算法—冒泡排序(Java实现)

    [toc] 冒泡排序 程序代码package com.uplooking.bigdata.datastructure;import java.util.Arrays;public class Bubb ...

  8. java冒泡法优化_数据结构java版之冒泡排序及优化

    冒泡排序的时间用大O表示法是O(N^2). 传统的冒泡排序: /** * @param total 要排序的数组长度 */ public void sort(int total){ int num[] ...

  9. 数据结构java学生成绩排序_数据结构学习--Java简单排序

    冒泡排序需要元素每次遍历都从最底部向上冒泡,找到适合的位置后,该位置之后的元素继续向上冒,这样一趟排序结束后,将冒出最大或者最小值. 选择排序需要元素从0号位开始向上遍历一遍,并将最小值放到0号位上, ...

最新文章

  1. windows下:ERROR 1366 (HY000): Incorrect string value: ‘\xC1\xF5\xB1\xB8‘ for column ‘name‘ at row 1
  2. linux中创建本地yum库,轻松安装Linux软件
  3. NOIp2018 Mission Failed Level F
  4. 中国氢能行业发展现状与投资规划深度研究报告2022-2027年版
  5. python加减乘除符号_Python项目如何合理组织规避import天坑
  6. python基础篇——元组
  7. 中国土壤厚度空间分布数据
  8. 惠普136nw打印机清零_HP惠普打印机清零大全
  9. bp神经网络预测案例python_详细BP神经网络预测算法及实现过程实例
  10. c语言程序设计数字电位器,可编程数字电位器在AVR单片机中的应用
  11. 【VRP问题】基于节约算法CW求解带容量的车辆路径规划问题(CVRP)
  12. 校招行测笔试-言语理解与表达
  13. 黄山学院计算机科学与技术怎么样,黄山学院怎么样?黄山学院点评及最新评价情况...
  14. java 将对象置空_Java 中将对象引用置 null 的作用?
  15. 2022年电工(初级)国家题库及在线模拟考试
  16. 831数据结构与c语言试题,2018年广东工业大学计算机院831数据结构与C语言[专硕]之C程序设计考研核心题库...
  17. “气球” 的最大数量
  18. Matlab求解李雅普诺夫(Lyapunov)方程
  19. 你还停留在使用Dagger2吗? 带你一步一步走进Dagger2的世界
  20. 电子词典的实现(一)

热门文章

  1. ServiceStack.Redis的问题与修正
  2. 人还是很需要成就感的
  3. pytorch实现性别检测
  4. 深入学习Java8 Lambda (default method, lambda, function reference, java.util.function 包)
  5. 快给你的app上锁吧(android图案解锁)
  6. 开博首发2017年1月13日开博大吉
  7. 《Node.js入门》Windows 7下Node.js Web开发环境搭建笔记
  8. SpringMVC _Controller认识(1)
  9. Yoda 表示法错在哪里
  10. 数据库-ADONET-在数据集DataSet中使用关系对象DataRelation处理关系