Java关于数据结构的实现:树

Java关于数据结构的实现:树

关于作者

郭孝星,非著名程序员,主要从事Android平台基础架构与中间件方面的工作,欢迎交流技术方面的问题,可以去我的Github提交Issue或者发邮件至[email protected]与我联系。

文章目录`

  • 一 树的概念与应用场景
    • 1.1 二叉查找树
    • 1.2 AVL树
    • 1.3 红黑树
    • 1.4 B树
  • 二 树的操作与源码实现
    • 2.1 TreeMap/TreeSet实现原理

更多文章:https://github.com/guoxiaoxing/data-structure-and-algorithm/blob/master/README.md

写在前面

之前在网上看到过很多关于Java集合框架实现原理文章,但大都在讲接口的作用与各类集合的实现,对其中数据结构的阐述的不多,例如红黑树的染色和旋转是怎么进行的等等,本篇文章从
数据结构的基本原理出发,逐步去分析Java集合里数据结构的应用与实现。

一 树的概念与应用场景

树是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由n(n>0)个有限节点组成一个具有层次关系的集合。把
它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

树有以下特点:

  • 每个节点有零个或多个子节点;
  • 没有父节点的节点称为根节点;
  • 每一个非根节点有且只有一个父节点;
  • 除了根节点外,每个子节点可以分为多个不相交的子树;

与树相关的概念

  • 节点的度:一个节点含有的子树的个数称为该节点的度;
  • 树的度:一棵树中,最大的节点的度称为树的度;
  • 叶节点或终端节点:度为零的节点;
  • 非终端节点或分支节点:度不为零的节点;
  • 父亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;
  • 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;
  • 兄弟节点:具有相同父节点的节点互称为兄弟节点;
  • 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
  • 深度:对于任意节点n, n的深度为从根到n的唯一路径长,根的深度为0;
  • 高度:对于任意节点n, n的高度为从n到一片树叶的最长路径长,所有树叶的高度为0;
  • 堂兄弟节点:父节点在同一层的节点互为堂兄弟;
  • 节点的祖先:从根到该节点所经分支上的所有节点;
  • 子孙:以某节点为根的子树中任一节点都称为该节点的子孙。
  • 森林:由m(m>=0)棵互不相交的树的集合称为森林;

注:参照亲戚关系,这些概念很好理解,家族谱也是一种树结构。��

树的分类

  • 无序树:树中任意节点的子节点之间没有顺序关系,这种树称为无序树,也称为自由树;
  • 有序树:树中任意节点的子节点之间有顺序关系,这种树称为有序树;

其中有序树又分为:

  • 二叉树:每个节点最多含有两个子树的树称为二叉树;
  • 完全二叉树:对于一颗二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树;
  • 满二叉树:所有叶节点都在最底层的完全二叉树;
  • AVL树:当且仅当任何节点的两棵子树的高度差不大于1的二叉树;
  • 二叉查找树:树中的每个节点,它的左子树中所有项小于X中的项,它的右子树中的所有项大于X中的项。
  • 霍夫曼树:带权路径最短的二叉树称为哈夫曼树或最优二叉树;
  • B树:一种对读写操作进行优化的自平衡的二叉查找树,能够保持数据有序,拥有多余两个子树。

1.1 二叉查找树

二叉查找树是一种有序二叉树,它的查找、插入的时间复杂度为O(logN)

二叉查找是是一种有序二叉树.

主要特点

  • 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值。
  • 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值。
  • 任意节点的左右子树叶为二叉查找树
  • 没有键值相等的节点。

就就是说二叉查找树上的节点是排好序的:左子树 < 根节点 < 右子树。

性能分析

  • 在最坏的情况下,即当先后插入的关键字有序时,构造成的二叉查找树蜕变为单支树,输的深度为n,其平均查找长度为(n+1)/2。
  • 在最好的情况下,二叉查找树的形态和折半查找的判定树相同,其时间复杂度为O(log2(N))。

我们来看看二叉查找树上的相关操作。

构造

当我们用一组数值构造一棵二叉查找树时,就相当于对这组数值进行了排序,在最坏情况下,即该组数值是从小到达排好序的,构造出来的二叉树的所有节点都
没有左子树,这种情况下的时间复杂度为O(N2)。(N的平方)

另外,树排序的问题使得CPU Cache性能较差,特别是当节点是动态内存分配时。而堆排序的CPU Cache性能较好。另一方面,树排序是最优的增量排序(incremental sorting)算法,
保持一个数值序列的有序性。

查找

由于二叉查找树具有左子树 < 根节点 < 右子树的特点,因此在二叉查找树b中查找x的过程如下:

  1. 若b是空树,则查找失败,否则:
  2. 若x等于根节点的值,则查找成功,否则:
  3. 若x小于b根节点的值,则查找左子树,否则
  4. 若x大于b根节点的值,则查找右子树。

整个流程是一个递归的过程。

插入

插入的过程也是查找的过程,在二叉查找树中插入节点x的过程如下:

  1. 若b是空树,则x作为根节点插入,否则:
  2. 若x的值等于根节点的值,则直接返回,否则:
  3. 若x的值小于根节点的值,则将x插入当该根节点的左子树中,否则
  4. 将x插入该根节点的右子树中。

这也是一个递归的过程,这里我们要关注两点:

  • 插入的过程也是查找的过程
  • 二叉查找树不允许有相同值的节点
删除

在二叉查找树上删除一个节点,分为三种情况:

  1. 若删除的是叶子节点,则不会破坏树的结构,只需要修改其双亲节点的指针即可。
  2. 若删除的节点只有一个孩子节点,则用它的孩子节点代替它的位置即可,如此也不会破坏红黑树的结构。
  3. 若删除的节点有两个孩子节点,这种情况复杂一下,我们通常会找到要删除节点X的左子树里的最大元素或者右子树里的最小元素,然后用M替换掉X,再删除节点,因为此时M最多只会有一个
    节点(如果左子树最大元素则没有右子节点,若是右子树最小元素则没有左子节点),若M没有孩子节点,直接进入情况1处理,若M只有一个孩子,则直接进入情况2处理。

另外,如果删除的次数不多,可以采用懒惰删除的方式,即当一个元素删除时,它仍然留在树中,只是被比较为已删除,这种方式在有重复项是特别有用,
另外如果删除的元素又重新插入,这种方式可以避免新单元的创建开销。

1.2 AVL树

AVL树是带有平衡条件的二叉查找树。

主要特点

  • AVL树中的任何阶段的两棵子树高度最大差别为1.

AVL树还有个平衡因子的概念,平衡因子 = 左子树高度 - 右子树高度,因此平衡因子为-1,0,1的为平衡二叉树,其他的都是不平衡的。

另外,把一棵不平衡的二叉查找树变成一棵平衡二叉树,我们称之为AVL旋转

我们来看看不同情况下AVL旋转如何进行。

  • 左左情况:右旋
  • 右右情况:左旋
  • 左右情况:先左旋,再右旋
  • 右左情况:先右旋,再左旋

注:所谓左左指的是左边的左子树多了一个,其他的依此类推。

具体操作如下所示,我们可以看到左左情况和右右情况只需要单旋转就可以完成,左右情况与右左情况需要先把它们变成左左情况与右右情况
再进行旋转,因此这两种情况需要双旋转才能完成。

性能分析

查找、插入与删除在平均和最坏的情况下的时间复杂度为O(logN)。

AVL树也是二叉查找树的一种,它的很多操作都可以向我们上面描述的二叉查找树的操作那样进行。删除操作有点例外,我们在进行删除操作
时可以把要删除的节点向下旋转形成一个叶子节点,然后直接删除这个叶子节点,因为旋转成叶子节点期间,做多有logN个节点被旋转,每次
AVL旋转花费的事件固定,所以删除操作的时间复杂度是O(logN)。

1.3 红黑树

红黑树是平衡二叉树的变种,它的操作的时间复杂度是O(logN).

红黑树是一种具有着色性质的树,具有以下特点:

  • 每个节点被着成红色或者黑色
  • 根是黑色的
  • 叶子节点都是黑色的,叶子节点指的是NULL节点,有些红黑树图中可能没有标记出来。
  • 如果一个节点是红色的,那么他额子节点必须是黑色的,也就是不会存在两个红色节点毗邻。
  • 从一个节点到一个叶子节点(NULL节点)的每一条路径必须包含相同数目的黑色节点。

红黑树也是一种二叉查找树,查找操作与二叉查找树相同,插入与删除操作有所不同。

1.4 B树

B树是一种自平衡的树,能够保持数据有序,B树为系统大块数据的读写操作做了优化,通常用在数据库与文件系统的实现上。

我们前面讲解了二叉查找树、AVL树,红黑树,这三种都是典型的二叉查找树结构,其查找的事件复杂度O(logN)与树的深度有关,考虑这么一种情况,如果有
大量的数据,而节点存储的数据有限,这种情况下,我们只能去扩充树的深度,就会导致查找效率低下。

怎么解决这种问题,一个简单的想法就是:二叉变多叉。

这里我们想象一下常见的文件系统,它也是一种树结构,在查找文件时,树的深度就决定了查找的效率。因此B树就是为了减少数的深度从而提高查找效率的一种
数据结构。

主要特点

一个阶为M的B树具有以下特点:

注:M阶指的是M叉查找树,例如M = 2,则为二叉查找树。

  • 数据项存储在树叶上
  • 非叶节点存储直到M-1个关键字以指示搜索方向:关键字代表子树i+1中最小的关键字
  • 树的根或者是一片树叶,或者其儿子数都在2和M之间。
  • 除根外,所有非树叶节点的儿子树在M/2与M之间。
  • 所有的树叶都在相同的深度上拥有的数据项都在L/2与L之间。

性能分析

B树在查找、插入以及删除等操作中,时间复杂度为O(logN)。

二 树的操作与源码实现

在文章01Java关于数据结构的实现:表、栈与队列中我们
讨论了ArrayList与LinkedList的实现,它们的瓶颈在于查找效率低下。因而Java集合设计了Set与Map接口,它们在插入、删除与查找等基本操作都有良好的表现。

2.1 TreeMap/TreeSet实现原理

TreeSet实际上是基于TreeMap的NavigableSet的实现,它在功能上完全依赖于TreeMap,TreeMap是一个基于红黑树实现的Map,它在存储时对元素进行排序。

因此只要理解了TreeMap实现即可,TreeSet在功能上完全依赖于TreeMap。

TreeMap具有以下特点:

  • TreeMap是一个有序的key-value集合,基于红黑树实现。
  • 没有实现同步

TreeMap实现以下接口:

  • NavigableMap:支持一系列导航方法,比如返回有序的key集合。
  • Cloneable:可以被克隆。
  • Serializable:支持序列化。
成员变量
//比较器
private final Comparator<? 
  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值