在计算机科学中, 树(tree) 是一种抽象数据类型或是实现这种抽象数据类型的数据结构, 用来模拟具有树状结构性质的数据集合. 它是由n(n>0)个有限节点组成一个具有层次关系的集合. 把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上, 而叶朝下的. 它具有以下的特点:
1. 每个节点有两个或者多个子节点;
2. 没有父节点的节点称为根节点;
3. 每一个非根节点有且只有一个父节点;
4. 除了根节点外, 每个子节点可以分为多个不想交的子树;
还有一些和树相关的术语: 度,叶子节点, 根节点, 父节点, 子节点, 深度, 高度
二叉树
二叉树: 每个节点最多包含两个子树的树称为二叉树
满二叉树: 除最后一层无任何子节点外, 每一层上的所有节点都有两个子节点.
完全二叉树: 若设二叉树的深度为h, 除第h层外, 其他各层的节点数都达到最大个数, 第h层所有的节点都连续集中在最左边, 这就是完全二叉树.
private static class TreeNode{
int val;
TreeNode left;
TreeNode right;
TreeNode(int x){val = x;}
}
二叉树遍历的方式:
先序遍历: 先根节点->遍历左子树->遍历右子树
中序遍历: 遍历左子树->根节点->遍历右子树
后序遍历: 遍历左子树->遍历右子树->根节点
深度优先搜索(DFS)与广度优先搜索(BFS)
实现: bfs=队列,入队列,出队列 一次访问一条路径; dfs=栈, 压栈, 出栈 一次访问多条路径
关系: 用DFS解决的问题都可以用BFS解决. DFS易于编写(递归), 时间消耗少但是容易发生爆栈, 而BFS可以控制队列的长度.
2动态查找树
2.1二叉查找树
二叉查找树是二叉树的衍生概念:
二叉查找树(Binary Search Tree) 也成为二叉搜索数,有序二叉树或排序二叉树, 指一棵空树或具有下列性质的二叉树:
- 若任意节点的左子树不空, 则左子树上所有节点的值均小于它的根节点的值;
- 若任意节点的右子树不空,则右子上所有节点的值均大于它根节点的值;
- 任意节点的左右子树也分别为二叉查找树
- 没有键值相等的节点
二叉查找树相比于其他数据结构的优势在于查找,插入的时间复杂度较低为O(log n) . 二叉查找树是基础性数据结构, 用于构建更为抽象的数据结构,如集合,多重集,关联数组等.
2.2平衡二叉树(AVL树)
平衡二叉树: 当且仅当任何节点的两棵子树的高度差不大于1的二叉树;
其中AVL树是最先发明的自平衡二叉查找树,是最原始典型的平衡二叉树.
平衡二叉树是基于二叉查找树的改进. 由于在某些极端的情况下(如在插入的序列是有序的时), 二叉查找树将退化成近似链或链, 此时,其他操作的时间复杂度将退化成线性的, 即O(n). 所有我们通过自平衡操作(即旋转)构建两个子树高度差不超过1的平衡二叉树.
2.3红黑树
红黑树也是一种自平衡的二叉查找树
- 每个节点要么是红的要么是黑的(红或黑)
- 根节点是黑的(根黑)
- 每个叶节点(叶节点指树尾端NIL指针或NULL节点) 都是黑的(叶黑)
- 如果一个节点是红的, 那么它的两个儿子都是黑的(红子黑)
- 对于任意节点而言,其到叶节点树尾端NIL指针的每条路径都包含相同数目的黑节点(路径下黑相同)
如图就是一棵典型的红黑树. 保证红黑树满足它的基本性质, 就是在调整数据结构自平衡.
而红黑树自平衡的调整操作方式就有旋转和变色两种
红黑树是一种应用很广的数据结构, 如在Java集合类中TreeSet和TreeMap的底层,C++STL中set与map, 以及linux中虚拟内存的管理
2.4哈夫曼树(Huffman Tree)
哈夫曼树是一种带权路径长度最短的二叉树, 也成为最优二叉树.
一般可以按下面步骤构建:
- 将所有左右子树都为空的作为根节点
- 在深林中选出两棵根节点的权值最小的树作为一棵新树的附件根节点的权值为其左右子树上根节点的权值只和. 注意,左子树的权值应小于右子树的权值.
- 从深林中删除这两棵树, 同时把新树加入到深林中
- 重复2 3 步骤, 直到森林中只有一棵树为止,此树变为哈夫曼树
哈夫曼编码就是哈夫曼树的应用. 即如何让电文中出现较多的字符采用尽可能短的编码且保证在译码时不出现歧义.
3多路查找树
大规模数据存储中, 实现索引查询这样一个实际背景下, 树节点存储的元素数量是有限的(如果元素数量非常多的话, 查找就退化成节点内部的线性查找了) , 这样导致二叉树结构由于树的深度过大而造成磁盘IO读写过于频繁, 进而导致查询效率低下.
3.1 B树(balanced tree)
B树(B-tree) 又称B-树、B_树是一种自平衡的多叉树, 能够保持数据有序. 这种数据结构能够让查找数据, 顺序访问, 插入数据及删除的动作, 都在对数时间内完成. B数, 概括来说是一个一般化的二叉查找树(binary search tree) , 可以最多拥有两个子节点. 与自平衡二叉查找树不同, B树适用于读写相对大的数据块存储系统, 如磁盘.
- 根节点至少有两个子女
- 每个中间节点都包含k-1个元素和k个孩子, 其中m/2<=k<=m
- 每一个叶子节点都包含k-1个元素, 其中m/2<=k<=m
- 所有的叶子节点都位于同一层
- 每个节点中的元素从小到大排列, 节点中k-1个元素正好是k个孩子包含的元素的值域分划.
如图所示就是一棵符合规范的B树, 由于相比于磁盘IO的速度,内存中的耗时几乎可以省略,所以只要树的高度足够低,IO的次数足够小,就可以提升查询性能
B树的增加删除同样遵循自平衡的性质, 有旋转和换位
B树的应用是文件系统及部分非关系型数据库索引
todo: B-树详细实现: link
3.2 B+ 树
B+树是一种树数据结构, 通常用于关系型数据库(如MySQL)和操作系统文件系统中. B+树的特点是能够保持数据稳定有序, 其插入与修改拥有较稳定的对数时间复杂度. B+树元素自底向上插入, 这与二叉树恰好相反.
在B树的基础上, 为叶子节点增加链表指针(B树+叶子有序链表), 所有关键字都在叶子节点中出现, 非叶子节点作为叶子节点的索引; B+树总是到叶子节点才命中.
B+树的非叶子节点不保存数据, 只保存子树的临界值(最大或最小), 所以同样大小的节点, B+树相对于B树能够有更多的分支, 使得这棵树更加矮胖, 查询时做的IO操作次数也更少.
这通常在多数节点在次级存储比如硬盘中的时候出现. 通过最大化在每个内部节点内的子节点的数目减少树的高度, 平衡操作不经常发生, 而且效率增加了.
3.3 B*树
B*树是B+树的变体, 在B+树的非根和非叶子节点再增加指向兄弟的指针
在B+树的基础上, 为非叶子节点也增加链表指针, 将节点的最低利用率从1/2 提高到2/3
3.4 R树
R树是用来做空间数据存储的树状数据结构. 例如给地理位置,矩形和多边形这类多维数据建立索引.
R树的核心思想是聚合距离相近的节点并在树结构的上一层将其表示为这些节点的最小外接矩形(MBR) , 这个最小外接矩形就成为上一层的一个节点. 因为所有节点都在它们的最小外接矩形中, 所以跟某个矩形不相交的查询就一定跟这个矩形中的所有点都不相交. 叶子节点的每个矩形都代表一个对象, 节点都是对象的聚合, 并且越往上层聚合的对象就越多. 也可以把每一层看做是对数据集的近似,叶子节点层是最细粒度的近似,与数据集相似度100%,越往上层越粗糙.