一、B树
粗略定义
B树是二叉树的拓展,相对二叉树,B树的孩子个数被定义为m,通过m-1个关键字key进行分割,例如 第一个结点的key:10 ,那么就把子树分成了小于10的部分和大于10的部分。根节点的右子树, key 14 20 , 就把子树分成了小于14;大于14 且 小于20 ; 大于20 的三个区间。 每个区间再对应一个结点。
定义规则
对于整棵树来说:
- 根节点至少1个key,2个子树
- 整棵树的阶m,规定了每个结点最大的孩子数。
- 为了维护平衡,所有非叶结点至少有 [m/2] 向上取整颗子树。m用于定义子树的最大数量。那么关键字key的个数为 [m/2]-1
- 所有的叶子结点都在同一层。
第二和第三条规则就限定了每个节点孩子个数和key个数的取值范围。
对于树上的一个结点来说:
- n表示关键字的个数
- keys集合:存放了关键字的数组,且满足 k1 < k2 < k3…
- p集合:存放了孩子的指针数组,p数组个数 = n +1 ,因为两个孩子夹着一个key
查找
假设查找t , 那么从根节点出发,遍历每个结点的keys, 找到第一个 key[i] > t ,的位置,然后递归到 p[i]子树; 如果找到key[i] == t ,那么直接返回 ; 如果找到尽头,那么进入p[n]子树。
插入关键字
假设插入的关键字是 {20,30,50,52,68,70} ,构造一颗m = 3 的三阶B树。
可以算出非叶结点的关键字取值范围:[m / 2] 向上取整 - 1 = 1, m - 1 = 2 。
第一步:在根节点中插入20,30
第二步:插入50,由于关键字个数大于2,需要进行分裂。
分裂是找到下标为n / 2的key,提到上一层。
转换后
第三步:接下去按照正常的思路,插入52,60,变成下面的状态
很明显50 52 60超过关键字最大值, 因此需要把52提到上一层。而且此时需要调整50 30 52 之间的关系。调整之后变成下面的样子。
第四步:插入68,70,调整到上一层之后,上层也超过限制,因此分裂是不断向上执行的。
插入70后,第一次分裂的结果。
第二次分裂
删除关键字
第一大类 删除的关键字在叶结点上
令 min = [m / 2 ] - 1 ,表示节点上关键字的最小合法值
1)如果当前keys的个数 > min, 那么直接删除掉关键字
2)如果当前keys <= min ,但是同层的左右兄弟之间存在 keys > min ,就从兄弟之间借结点。例如下图的删除 2
借过来之后还要调整 5 7 9的相对位置, 下面是删除完并且调整之后的状态。
3)如果同层的兄弟子树 key的个数 <= min , 那么需要进行结点合并。
例如下面的删除16,那么合并的方式有两种。
第一种,把父结点的最左边的关键字转移到下一层。效果是这样的
第二种做法是把父结点的最右边的关键字转移到下一层。效果是这样的。
第二大类 删除的关键字不在叶结点上
这大类的做法是把关键字转移到叶结点上。
如何交换呢? 交换之前要介绍相邻关键字,它就是当前结点左子树中最大值,和右子树中的最小值。 也就是根节点的前驱、后继。
接着把根节点和前驱或者后继进行交换,那么就转移到了第一类的情况,而且属于第一类的第一种情况。
二、B+树
广泛用于数据库和文件系统的查找。
B树B+树的区别
1、具有n个关键字,那么就有n颗子树,即每一个关键字对应一个子树。
2、每个结点的关键字个数n : [m/2] 向上取整 <= n <= m ,根节点 1<= n <= m。因为关键字和子树的个数相等,前面这两则公式也是子树的取值范围。
3、非叶子结点只起索引作用, 叶子结点包含信息。在B树中,所有的结点的key都用于保存值,方便查找;但是在B+树,就只有叶子结点保存值的地址,也就是对应着记录。
4、B+树中有一个单链表,表头的下一位指向做小的关键字,然后在叶子结点串成一个单链表。
查找/遍历过程
B+树的关键字和子树是一一对应的,它表示p[i]对应的子树,关键字最大值为key[i]。
因此假设要查找num,那么就要在结点中找到第一个key[i] > num的位置,然后进入i-1的子树。
三、红黑树
插入删除后,通过调整可以重新变得平衡。
1、红黑树是一颗平衡二叉搜索树,中序遍历单调不减,同时它的本质是B树,还是个234B树。
2、节点是红色或者黑色
3、根节点是黑色
4、每个叶节点或者叫外部节点(不在树上的,NULL节点,空节点),是黑色的
5、每个红色节点的两个子节点都是黑色的。(换句话说,红节点不可能是父子关系)
6、从根节点到每个叶子节点的所有路径上的黑色节点个数相等。(也称黑高度)
插入一个节点,必须插入红色节点,这样只影响了条件4,只要再进行调整就能恢复平衡
每个红黑树都有一个对应的B树,是个234树(234个子树)
3.1 左旋 以父节点为轴,把右子树向左边甩上去,根节点变成左子树
左旋是把以X为中轴,把右子树y,顺时针向左旋转到x的父节点,使y的左子树等于x。而且y原本的左子树称为x的右子树。