树
Posted xxMYxx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树相关的知识,希望对你有一定的参考价值。
第4章 树
对于大量的输入数据,链表的线性访问时间太慢,本章谈论一种简单的数据结构,其大部分操作的运行时间平均为0(log N)
二叉查找树是两种库集合类TreeSet和TreeMap实现的基础
4.1 预备知识
树可以用几种方式定义,定义树的一种自然方式是递归的方式。一棵树是一些节点的集合,这个集合可以是空集,若不是空集,则树由称作根(root)的节点r以及0个或者多个非空的子树组成
4.1.1树的实现
实现树的一种方法可以在每一个节点处数据外还要有一个链,使得该节点的每一个儿子都有一个链指向它(因为每一个节点的儿子树可以变化很大,所有不是很好的实现)
将每一个节点的所有儿子都放在该树节点的链表中
4.1.2 树的遍历及运用
树的遍历策略有
先序遍历 :在先序遍历中,对节点的处理工作是在它的诸多儿子节点被处理之前进行的运行时间为O(N)
后序遍历 :在后序遍历中,一个节点处的工作是在它的诸儿子节点被计算后进行的
4.2 二叉树
二叉树是一颗树,其中每一个节点都不能有多余2个儿子
二叉树的一个性质是一棵平均二叉树的深度要比节点个数N小的多,其平均深度为O(根号N),而对于特殊类型的二叉查找树,其深度的平均值时O(log N)
4.2.1 实现
我们可以保存直接链接到它们的链,树节点的声明在结构上类似于双链表的声明
4.2.2 例子:表达式树
表达式的树叶表示操作数,节点表示操作符,
4.3 查找树ADT----二叉查找树
二叉树的一个重要的应用是它们查找中的使用
对于书中的每个节点X 它的左叶子树的值都小于X中的项,而右侧的项都比X中的项大
4.3.4 remove方法
正如许多数据结构一样,,最困难的操作时remove,删除有几种可能
1. 如果节点是树叶,那么它可以被直接删除
2. 如果节点有一个儿子,则该节点可以在其父类节点做出调整,跳过直接的链绕过该节点
3. 复杂情况是处理具有两个儿子的节点,一般的删除策略是用其右子树的最小的数据代替该节点的数据并递归的删除那个节点
懒惰删除 : 当一个元素要被删除时,它任然留在树中,只是被标记为删除(耗时小,其次下次如果插入相同数据时,可以避免重新分配一个新单元)
4.3.5 平均情况分析
我们期望所有操作花费时间都是O(log N)、
多有操作允许时间都是O(d),d为访问节点的深度
一棵树的所有节点的深度被称为内部路径长
正常删除算法使得 左树的深度要比右树的深度深(删除时将右子树最小的节点代替该节点)
4.4 AVL树
AVL树是带有平衡条件的二叉树,这个平衡条件必须要容易保持,而且它保证树的深度须是O(log N)最简单的想法是保证书的左右两个子树具有相同的高度,另一种平衡条件必须有相同高度的左子树和右子树
虽然这种平衡条件保证树的深度小,但是它太严格而难以使用
一棵平衡树的每个节点的左子树最多比右子树高一层
AVL树的高度略大于log N,出去可能的删除了懒惰删除外,所有操作都可以以时间O(log N)执行
假如插入一个树破坏平衡后,那么在下次插入之前恢复平衡,这总可以通过树进行简单的修正来做到,我们称之为旋转
不平衡可能有4种情况
1. 对X的左子树的左儿子进行一次插入
2. 对X 的左子树的右儿子进行一次插入
3. 对X的右子树的左儿子进行一次插入
4. 对X 的右子树的右儿子进行一次插入
1 和 4 2 和 3 关于 X 对称
第1种情况和第4种情况,该情况通过对树进行一次单旋转而完成跳转
对于第2种和第3中情况来说 需要对树进行双旋转完成
4.4.1 单旋转
将左子树上调作为父子树,将父子树调为右子树。左子树的右节点调整到右子树的左节点
4.4.2 双旋转
将左子树的右节点上调至父子树,左子树的右节点上调至父子树的左子树调至左子树的左节点的右子树 将左子树的右节的右子树调至右子树的左节点
4.5 伸展树
它保证从空树开始连续M此对树的操作最多花费 O(Mlog N)时间,虽然不排除单次操作花费O(N)时间
伸展树的基本想法是 当一个节点别访问后,它就要经过一系列AVL树的旋转被推倒根上。适合于重复访问的元素使用
当访问路劲长而且导致超出正常查询时间的时候,这些旋转将对未来有利,当访问耗时很少的时候,这些旋转就不那么有益
4.7 B树
阶为M的B+树具有如下的特性的树
1. 数据项存在树叶上
2. 非树叶节点存储直到M-1个关键字以指示搜索方向
3. 树的根或者是一片树叶,或者其儿子树在2和M之间
4. 除根外,所有非树叶节点的儿子树在M/2 到M之间
5. 所有的树叶都有相同的深度并有L/2 和L之间的数据项
4.8 标准树中集合与映射
4.8.1 关于Set接口
排序假设TreeSet中的项实现Comparable接口,另一种排序可以通过Comparator实例化Tree来确认
4.8.2 关于Map接口
get返回Map中的key相关的值,或当key 不存在时返回null,如果在Map中不存在null可以使用get()来确认该key是否存在在该Map中 ,否则只能使用containsKey()
通过Map迭代要比Collection复杂,因为Map不提供迭代器,而是提供3种方法,将Map对象视图最为Collection对象返回,
java要求TreeMap和TreeSet支持基本的add remove contains 操作以对数最坏情景时间完成,因此基本实现都是使用平衡二叉查找树(自顶向下红黑树)
4.8.3 TreeMap和TreeSet类的实现
实现TreeMap和TreeSet的一个重要问题是提供对迭代器类的支持,困难部分是到下一节点高效的推进,存在几个可能解决的方案
1. 在构造迭代器时,让每一个迭代器把包含TreeSet想的数组作为该迭代器的数据存储(我们可以使用toArray并不需要迭代器)
2. 让迭代器保留存储通向当前节点的路径上的节点的一个栈,根据该信息,可以推出迭代器的下一个节点
以上是关于树的主要内容,如果未能解决你的问题,请参考以下文章