基础概念
树
存储的是具有“一对多”关系的数据元素的集合
树的深度(高度)
从一棵树的树根开始,树根所在层为第一层,根的孩子结点所在的层为第二层,依次类推, 一棵树的深度(高度)是树中结点所在的最大的层次
兄弟节点
都有相同的父结点的子结点
阶
整棵树, 子节点最多的个数是m,那么这棵树就是m阶树。
二叉树
满足以下两个条件的树:
- 本身是有序树;
- 树中包含的各个节点的度不能超过 2,即只能是 0、1 或者 2;
拥有的子树数(结点有多少分支)称为结点的度(Degree)
二叉树的性质:
- 二叉树中,第 n 层最多有 2n-1 个结点。
- 如果二叉树的深度为 n,那么此二叉树最多有 2n-1 个结点。
- 二叉树中,终端结点数(叶子结点数)为 n0,度为 2 的结点数为 n2,则 n0=n2+1。
满二叉树
如果二叉树中除了叶子结点,每个结点的度都为 2,则此二叉树称为满二叉树
满二叉树的性质:
- 满二叉树中第 n 层的节点数为 2n-1 个。
- 深度为 n 的满二叉树必有 2n-1 个节点 ,叶子数为 2n-1。
- 满二叉树中不存在度为 1 的节点,每一个分支点中都两棵深度相同的子树,且叶子节点都在最底层。
- 具有 n 个节点的满二叉树的深度为 log2(n+1)
完全二叉树
如果二叉树中除去最后一层节点为满二叉树,且最后一层的结点依次从左到右分布,则此二叉树被称为完全二叉树。
二叉排序树 / 二叉查找树
二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree)
一棵空树,或者是具有下列性质的二叉树
(1)若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
B树
B-tree (中间的短线是英文连接符), 全称Balance-tree(平衡多路查找树)
一颗m阶B树,或为空树,或为满足下列特性的m叉树。
-
树中每个结点最多含有m棵子树;
-
若根结点不是叶子结点,则至少有两颗子树;
-
除根结点之外的所有非叶子结点至少有p个子节点(⌈m/2⌉ ≤ p ≤ m, ⌈m/2⌉为向上取整, 也可以用
ceil(m/2)
表示); -
所有的非叶子结点中包含以下数据:( n \color{#52c41a}{n} n, A \color{#1890ff}A A0, K \color{#fa8c16}K K1, A \color{#1890ff}A A1, K \color{#fa8c16}K K2,…, K \color{#fa8c16}K Kn, A \color{#1890ff}A An)
- ⌈m/2⌉ ≤ n ≤ m
- Ki(1≤i≤n)为关键字,且关键字按升序排序
- Ai为 指向子树的指针, 且Ai-1指向的子树中所有结点的关键码均小于Ki, An指向的子树中所有节点结点的关键码均大于Ki ( 即:每个数据Ki两旁的指针,左边指针的结点的关键码均小于Ki, 右边指针的结点的关键码均大于Ki)
-
所有的叶子结点都出现在同一层次上,即所有叶节点具有相同的深度,等于树高度。并且不带信息(可以看作是外部结点或查找失败的结点,实际上这些结点不存在,指向这些结点的指针为空), 如下图所示
type Node struct {
KeyNum int // 结点关键字个数
Keys KeyType // 关键字数组, Keys[0]不使用
parent *Node // 父结点
children *Node // 子结点
}
B+树
m阶的B+树的特征:
-
有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。
-
所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
-
所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素
B树和B+树的区别
- B+树的非叶子节点不保存关键字记录的指针,只进行数据索引,这样使得B+树每个非叶子节点所能保存的关键字大大增加;
- b+树查询必须查找到叶子节点,b树只要匹配到即可不用管元素位置,因此b+树查找更稳定
B+树的优势
- 层级更少: 相较于B树B+每个非叶子节点存储的关键字数更多,树的层级更少所以查询数据更快;
- 查询速度更稳定: B+所有关键字数据地址都存在叶子节点上,所以每次查找的次数都相同所以查询速度要比B树更稳定;
- 排序功能: B+树所有的叶子节点数据构成了一个有序链表,在查询大小区间的数据时候更方便,数据紧密性很高,缓存的命中率也会比B树高。
- 全节点遍历更快: B+树遍历整棵树只需要遍历所有的叶子节点即可,而不需要像B树一样需要对每一层进行遍历,这有利于数据库做全表扫描。
- IO次数更少: 单一节点存储更多的元素,使得查询的IO次数更少
二叉树的遍历
先序遍历
遍历原则
根-左-右
- 访问根节点
- 访问当前节点的左子树
- 若当前节点无左子树,则访问当前节点的右子树
遍历结果&解析
结果:
1 2 4 5 3 6 7
解析:
- 访问根节点,找到 节点 1 \color{#1890ff}{1} 1
- 访问节点 1 的左子树,找到节点 2 \color{#1890ff}{2} 2
- 访问节点 2 的左子树,找到节点 4 \color{#1890ff}{4} 4
- 访问节点 4 左子树失败 => 访问节点 4 右子树失败 => 以节点 4 为根节点的子树 遍历完成。回到以节点 2为根节点的子树, 还没遍历其右子树,开始遍历,找到节点 5 \color{#1890ff}{5} 5
- 访问节点 5 左子树失败 => 访问节点 5 右子树失败 => 以节点 5 为根节点的子树 遍历完成 => 以节点 2 为根节点的子树也遍历完成。回到以节点 1 为根节点的子树,并遍历其右子树,找到节点 3 \color{#1890ff}{3} 3
- 访问节点 3 左子树,找到节点 6 \color{#1890ff}{6} 6
- 访问节点 6 左子树失败 => 访问节点 6 右子树失败 => 以节点 6 为根节点的子树 遍历完成。以节点 3为根节点的子树 还没有遍历其右子树,因此现在开始遍历,找到节点 7 \color{#1890ff}{7} 7
- 访问节点 7 左子树失败 => 访问节点 7 右子树失败 => 以节点 7 为根节点的子树 遍历完成 => 遍历完成因此以节点 3 为根节点的子树遍历完成,同时回归节点 1。由于节点 1 的左右子树全部遍历完成,因此整个二叉树 遍历完成 \color{#52c41a}遍历完成 遍历完成
中序遍历
遍历原则
左-根-右
- 访问当前节点的左子树
- 访问根节点
- 访问当前节点的右子树
遍历结果&解析
结果:
4 2 5 1 6 3 7
解析:
- 访问根节点,找到节点 1 \color{#fa8c16}{1} 1
- 遍历节点 1 的左子树,找到节点 2 \color{#fa8c16}{2} 2
- 遍历节点 2 的左子树,找到节点 4 \color{#fa8c16}{4} 4
- 节点 4 无左孩子,因此找到节点 4 \color{#1890ff}{4} 4,并遍历节点 4 的右子树
- 节点 4 无右子树,因此节点 2 的左子树遍历完成,访问节点 2 \color{#1890ff}{2} 2
- 遍历节点 2 的右子树,找到节点 5 \color{#fa8c16}{5} 5
- 节点 5 无左子树,因此访问节点 5 \color{#1890ff}{5} 5 ,又因为节点 5 没有右子树,因此节点 1 的左子树遍历完成,访问节点 1 \color{#1890ff}{1} 1,并遍历节点 1 的右子树,找到节点 3 \color{#fa8c16}{3} 3
- 遍历节点 3 的左子树,找到节点 6 \color{#fa8c16}{6} 6
- 节点 6 无左子树,因此访问节点 6 \color{#1890ff}{6} 6,又因为该节点无右子树,因此节点 3 的左子树遍历完成,开始访问节点 3 \color{#1890ff}{3} 3,并遍历节点 3 的右子树,找到节点 7 \color{#fa8c16}{7} 7
- 节点 7 无左子树,因此访问节点 7 \color{#1890ff}{7} 7,又因为该节点无右子树,因此节点 1 的右子树遍历完成,即 遍历完成 \color{#52c41a}遍历完成 遍历完成
后序遍历
遍历原则
左-右-根
- 遍历当前节点的左子树
- 遍历当前节点的右子树
- 访问根节点
遍历结果&解析
结果:
4 5 2 6 7 3 1
解析:
- 从根节点 1 开始,遍历该节点的左子树(以节点 2 为根节点)
- 遍历节点 2 的左子树(以节点 4 为根节点)
- 节点 4 无左右子树,访问节点 4 \color{#1890ff}{4} 4,回到节点 2 ,遍历节点 2 的右子树(以 5 为根节点);
- 节点 5 无左右子树,访问节点 5 \color{#1890ff}{5} 5 ,节点 2 的左右子树也遍历完成,访问节点 2 \color{#1890ff}{2} 2
- 回到节点 1 ,开始遍历节点 1 的右子树(以节点 3 为根节点)
- 遍历节点 3 的左子树(以节点 6 为根节点)
- 节点 6 无左右子树,访问节点 6 \color{#1890ff}{6} 6,回节点 3,开始遍历节点 3 的右子树(以节点 7 为根节点)
- 节点 7 无左右子树,访问节点 7 \color{#1890ff}{7} 7,节点 3 的左右子树遍历完成,访问节点 3 \color{#1890ff}{3} 3;节点 1 的左右子树遍历完成,访问节点 1 \color{#1890ff}{1} 1, 遍历完成 \color{#52c41a}遍历完成 遍历完成
层次遍历
遍历原则
按照二叉树中的层次从左到右依次遍历每层中的结点。
实现思路是:
通过使用队列的数据结构,从树的根结点开始,依次将其左孩子和右孩子入队。
而后每次队列中一个结点出队,都将其左孩子和右孩子入队,直到树中所有结点都出队,出队结点的先后顺序就是层次遍历的最终结果。
遍历结果&解析
结果:
1 2 3 4 5 6 7
解析:
-
根结点 1 入队
队列内容: 1
-
根结点 1 \color{#1890ff}{1} 1 出队,出队的同时,将左孩子 2 和右孩子 3 分别入队
队列内容: 2 3
-
队头结点 2 \color{#1890ff}{2} 2 出队,出队的同时,将结点 2 的左孩子 4 和右孩子 5 依次入队
队列内容: 3 4 5
-
队头结点 3 \color{#1890ff}{3} 3 出队,出队的同时,将结点 3 的左孩子 6 和右孩子 7 依次入队
队列内容: 4 5 6 7
-
不断地循环,直至队列内为空。