搜索树:左子节点<节点<右子节点。
B-树:多路搜索树。
B+树:B-树的变体,更适用于文件系统,如mysql数据库。具体的,适合通过叶子节点的链指针进行区间查找。
B*树:B+树变体,提高了空间使用率 1 / 2 → 2 / 3 1/2→2/3 1/2→2/3。
参考文章:一文详解 B-树,B+树,B*树
对于一颗m阶B-树(上图m=3)
特点:
对于一颗m阶B*树(上图m=3)
与B+树的不同:
搜索树:左子节点<节点<右子节点。有些有序的数据搭建成二叉搜索树后,可能会退化成了一个链表。AVL树通过控制平衡因子在[-1,1]内来避免退化。
AVL树:平衡二叉搜索树。即左右子树的高度差小于等于1,并且其子树也是平衡二叉树。
平衡因子:节点的左子树高度 - 节点的右子树高度。用于判断节点的平衡状态。
影响平衡因子的操作:删除节点、添加节点。
2种基础的平衡化操作:左旋、右旋。
4种需要平衡操作的情况:LL、RR、LR、RL。
需要平衡的情况 | 状态 | 操作 |
---|---|---|
LL | 平衡因子为2,左子树平衡因子为1 | 右旋 |
RR | 平衡因子为-2,右子树平衡因子为-1 | 左旋 |
LR | 平衡因子为2,左子树平衡因子为-1 | 对左子树左旋、再对该节点右旋 |
RL | 平衡因子为-2,右子树平衡因子为1 | 对右子树右旋、再对该节点左旋 |
参考文章:详解 AVL 树(基础篇)
逆时针旋转,如果节点B有左子树。则将B的左子树作为A的右子树。
顺时针旋转,如果节点B有右子树。则将B的右子树作为C的左子树。
状态:节点n的平衡因子为2,左节点i的平衡因子为1。
平衡操作:对节点n进行一次右旋操作。
状态和操作与LL对称。
状态:节点n的平衡因子为2,左节点i的平衡因子为-1。
平衡操作:1先对节点i进行一次左旋操作,2再对节点n进行一次右旋操作。
状态和操作与LR对称。
搜索树:左子节点<节点<右子节点。有些有序的数据搭建成二叉搜索树后,可能会退化成了一个链表,时间复杂度最高是O(n)。AVL树通过控制平衡因子在[-1,1]内来避免退化,使得其查找的时间复杂度是O(logn),但是增删节点时的平衡成本很高,所以出现了红黑树。
红黑树:自平衡二叉搜索树。
叶子节点:null节点,即叶子节点没有数据。所以下图中那些没有子节点的非null节点,默认是有2个null的叶子节点。
参考文章:最通俗易懂入门红黑树(R-B Tree)、
图解红黑树的插入及调整过程+源码解读、 红黑树(二):删除操作
红黑树特点:
上述特点得出的一些推论:
红黑树也是二叉搜索树,当有新的节点时,根据最小右大的规则先找到它的插入位置(即它的父节点以及它是左孩子还是右孩子),然后标记为红色,最后根据红黑树的特点,进行变色和旋转。
变色和旋转的判断规则:
情况 | 变色 | 旋转 |
---|---|---|
父节点是黑色 | 无 | 无 |
父节点是红色且叔父节点是红色 | 有 | 无 |
父节点是红色且叔父节点是黑色 | 有 | 有 |
例如上图,插入51。此时,49和51都是红色,所以不满足特点4,需要进行变色。
第一步,应该就是将父节点49和叔父节点43变成黑色,以满足特点4;
第二步,将祖父节点45变成红色,使得更上层的节点满足特点5。
第三步,此时递归到祖父节点,判断其是否出现违反特点4的情况,有则重复第一步和第二步,没有则执行第4步。
第四步:如果根节点变成了红色,重新染黑。
如果插入65,则其会成为66的左孩子。此时,叔父节点null是黑色,导致父节点66变成黑色后,该分支的路径比叔父节点的路径多了1个黑色,不满足特点5,所以需要进行旋转。其实旋转的目的和AVL树一样,插入节点后导致出现平衡因子=2或-2的情况,需要平衡。
不平衡情况有LL、RR、LR、RL四种,在AVL树中有说明。对于红黑树,旋转(LR和RL需要先旋转变为LL和RR)-变色-旋转。
LL:插入65。先变色,然后对祖父节点69进行右旋。
RR:插入70。先变色,然后对祖父节点66进行左旋。
LR:插入67。先对父节点左旋变成LL,然后根据LL的操作先变色再对祖父节点69右旋。
RL:插入68。先对父节点右旋变成RR,然后根据RR的操作先变色再对祖父节点66右旋。
删除比插入要考虑更多因素。首先,删除可能删的是中间层的节点,其次,删除的节点可能是黑色。
按照删除的位置,有3种情况,有2个孩子、有1个孩子、没有孩子。
情况 | 处理方式 | 是否按颜色分类处理 |
---|---|---|
有2个孩子 | 先根据中序遍历找到后继节点(右子树中最小的节点),然后替换掉。此时情况转变为(有1个孩子、没有孩子),按照这2种情况处理。 | 看后继节点是否有孩子 |
有1个孩子 | 直接删除,然后让父节点指向子节点,子节点变色 | 否 |
没有孩子 | 根据节点(当前节点、父节点、兄弟节点)颜色分情况处理 | 是 |
如果要删除节点-1,则先跟后继节点0替换,问题转化为删除0(没有孩子),需要考虑节点颜色进行处理。
如果要删除节点4,则先跟后继节点5替换,问题转化为删除5(有1个孩子),不需要考虑节点颜色。
删除节点D
颜色判断:D一定是黑色,孩子一定是红色。
反证:如果该孩子是黑色,那么节点D的2个分支路径黑色节点数不相等(1≠2),不满足特点5。所以孩子是红色,再根据特点4,D是黑色。
操作:删除D,然后将子节点变黑色并作为父节点的新子节点。
需要根据节点颜色,分6种情况。
由于对称的情况操作是类似的,所以这里后3种情况都是以删除节点是其父亲节点的左子节点来讨论。
删除节点D
颜色判断:根据特点4,父节点必然是黑色。
操作:直接删除即可,不影响特点5。
删除节点D
颜色判断:根据特点4,当前节点和其兄弟节点为黑色。
操作:先删除节点;然后将父节点变成黑色;最后将兄弟节点变成红色。
删除节点D
颜色判断:兄弟节点的孩子为红色,否则父节点的左右分支不满足特点5。
操作:先删除节点D;然后判断父节点P是RL还是RR类型,如果是RL则右旋兄弟节点B得到RR类型;对父节点P、兄弟节点B和兄弟孩子节点BR进行变色(如果是RL转RR的不需要对BR变色);对父节点P左旋。
删除节点B
颜色判断:兄弟节点的孩子为红色,否则父节点的左右分支不满足特点5。
操作:先删除节点B;此时父节点P是RL类型,需要进行右旋得到RR,并且对节点D和DL变色使得满足特点4;接着对节点P进行做左旋;最后对DL进行变色使得满足特点5。
删除节点B
颜色判断:同2.3.4。
操作:比2.3.4少一次右旋。先删除节点B;接着对节点P进行做左旋;最后对DR进行变色使得满足特点5。
删除节点B
操作:先删除节点B,然后将兄弟节点D染成黑色。此时,P这个分支满足特点4和5,但是P的更上层左右分支不满足特点5,所以需要向上递归。
递归过程:如果P的父节点是红色,则根据2.3.2和2.3.3来处理。如果是黑色,则根据2.3.4/2、2.3.5、2.3.6处理。(这里可能还得再细化一下,使得具体操作可以递归处理)