标签:bbs weixin 增加 tor 默认 style key 因子 并且
红黑树(Red Black Tree)是一种自平衡的二叉搜索树(Self-balancing Binary Search Tree)。以前也叫做平衡二叉 B 树(Symmetric Binary B-tree)。
树的知识框架结构如下图所示:
平衡二叉搜索树(Balanced Binary Search Tree),英文简称BBST。经典常见的平衡二叉搜索树是 AVL 树和红黑树。
二叉搜索树(Binary Search Tree)是二叉树的一种,英文简称 BST。又称为二叉查找树、二叉排序树。
它的特点是任何一个结点的值都大于其左子树的所有结点的值,任何一个结点的值都小于其右子树的所有结点的值。
平衡(Balance):就是当结点数量固定时,左右子树的高度越接近,这棵二叉树越平衡(高度越低)。而最理想的平衡就是完全二叉树/满二叉树,高度最小的二叉树。
一棵二叉搜索树平均时间复杂度可以认为是树的高度 O(h)。像左边这棵,结点的左右子树的高度接近,属于一棵平衡二叉搜索树,O(h) = O(logn);而右边这棵,高度达到了最大,已经退化成了链表,O(h)=O(n)。
当二叉树退化成链表时,性能是很低的,所以我们需要在结点的插入、删除操作之后,想办法让二叉搜索树恢复平衡(减小树的高度)。
但是如果为了追求最理想的平衡,而增加了时间复杂度也不是很有必要,因此比较合理的方案就是:用尽量少的调整次数达到适度平衡。
由此引申出 AVL 树的概念。
AVL 树是最早发明的自平衡二叉搜索树之一,它取名自两位发明家的名字:G.M.Adelson-Velsky 和 E.M.Landis。
平衡因子(Balance Factor):某结点的左右子树的高度差。
每个叶子结点的平衡因子都是 0。看这棵二叉搜索树,红色数字标注了每个结点对应的平衡因子。
举例:8 的左子树高度为 2,右子树高度为 1,因此它的平衡因子为 1;5 的左子树高度为 0,右子树高度为 3,因此它的平衡因子为 -3;4 的左子树高度为 2,右子树高度为 4,因此它的平衡因子为 -2;
再看这棵 AVL 树和它每个结点对应的平衡因子:
可以看到 AVL 树具有以下特点:
每个结点的平衡因子只可能是 -1、0、1(如果绝对值超过 1,则认为是失衡);
每个结点的左右子树高度差不超过 1;
搜索、插入、删除的时间复杂度是 O(logn)。
B 树(Balanced Tree)是一种平衡的多路搜索树,多用于文件系统、数据库的实现。
这是一个简单的 3 阶 B 树:
1 个结点可以存储超过 2 个元素,可以拥有超过 2 个子结点;
拥有二叉搜索树的一些性质;
平衡,每个结点的所有子树高度一致;
比较矮。
m 阶 B 树指的是一个结点最多拥有 m 个子结点。假设一个结点存储的元素个数为 x,那么如果这个结点是:
根结点:1 ≤ x ≤ m - 1
非根结点:┌ m / 2 ┐ - 1 ≤ x ≤ m - 1
如果有子结点,子结点个数为 y = x + 1,那么如果这个结点是:
根结点:2 ≤ y ≤ m
非根结点:┌ m / 2 ┐ ≤ y ≤ m
向上取整(Ceiling),指的是取比自己大的最小整数,用数学符号 ┌ ┐ 表示;向下取整(Floor),指的是取比自己小的最大整数,用数学符号 └ ┘ 表示。
这是一棵二叉搜索树,通过某些父子结点合并,恰好能与上面的 B 树对应。
我们可以得到结论:
红黑树是一种含有红黑结点并能自平衡的二叉搜索树。
为了保证平衡,红黑树必须满足以下性质:
每个结点是要么是红色或黑色
根结点必须是黑色
叶结点(外部结点、空结点)是黑色
红色结点不能连续(也就是,红色结点的孩子和父亲都是黑色)
对于每个结点,从该点至 nil(树尾端,Java 中为 null 的结点)的任何路径都包含所相同个数的黑色结点
Answer 1:
1. 如果插入一个node引起了树的不平衡,AVL和RB-Tree都是最多只需要2次旋转操作,即两者都是O(1);但是在删除node引起树的不平衡时,最坏情况下,AVL需要维护从被删node到root这条路径上所有node的平衡性,因此AVL需要旋转的量级O(logN),而RB-Tree最多只需3次旋转,RB-Tree只需要O(1)的复杂度。
2. 其次,AVL的结构相较RB-Tree来说更为平衡,在插入和删除node更容易引起Tree的unbalance,因此在大量数据需要插入或者删除时,AVL需要rebalance的频率会更高。因此,RB-Tree在需要大量插入和删除node的场景下,效率更高。自然,由于AVL高度平衡,因此AVL的search效率更高。
3. map的实现只是折中了两者在search、insert以及delete下的效率。总体来说,RB-tree的统计性能是高于AVL的。
作者:Acjx
链接:http://www.zhihu.com/question/20545708/answer/58717264
Answer 2: (这个总结比较好)
红黑树的查询性能略微逊色于AVL树,因为他比avl树会稍微不平衡最多一层,也就是说红黑树的查询性能只比相同内容的avl树最多多一次比较,但是,红黑树在插入和删除上完爆avl树,avl树每次插入删除会进行大量的平衡度计算,而红黑树为了维持红黑性质所做的红黑变换和旋转的开销,相较于avl树为了维持平衡的开销要小得多
作者:陈智超
链接:http://www.zhihu.com/question/43744788/answer/98258881
Answer 3 :
功能、性能、空间开销的折中结果。
AVL,更平衡,结构上更加直观,时间效能针对读取而言更高;维护稍慢,空间开销较大。
红黑树,读取略逊于AVL,维护强于AVL,空间开销与AVL类似,内容极多时略优于AVL,维护优于AVL。
基本上主要的几种平衡树看来,红黑树有着良好的稳定性和完整的功能,性能表现也很不错,综合实力强,在诸如STL的场景中需要稳定表现。
作者:Coldwings
链接:http://www.zhihu.com/question/20545708/answer/44370878
根据上面的性质,可以画出这样一棵红黑树。接下来对红黑树做等价变换,即将所有的红色结点上升一层与它的父结点放在同一行,这就很像一棵 4 阶 B 树,转换效果如下图所示。
可以得出结论:
红黑树与 4 阶 B 树(2-3-4树)具有等价性
黑色结点与红色子结点融合在一起,形成 1 个 B 树结点
红黑树的黑色结点个数与 4 阶 B 树的结点总个数相等
当我们对一棵平衡二叉搜索树进行插入、删除的时候,很可能会让这棵树变得失衡(最坏可能导致所有祖先结点失衡,但是父结点和非祖先结点都不可能失衡)。
为了达到平衡,需要对树进行旋转。而红黑树能够达到自平衡,靠的也就是左旋、右旋和变色。
旋转操作是局部的。当一侧子树的结点少了,向另一侧“借”一些结点;当一侧子树的结点多了,则“租”一些结点给另一侧。
为了更清楚地讲解这部分内容,先声明几个概念:
左旋指的是以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点,右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变。
不考虑结点颜色,可以看到左旋只影响旋转结点和其右子树的结构,把右子树的结点往左子树移动。
右旋指的是以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变。
不考虑结点颜色,可以看到右旋只影响旋转结点和其左子树的结构,把左子树的结点往右子树移动。
变色指的是结点的颜色由红变黑或由黑变红。
将左旋、右旋和变色结合起来,得到一套变换规则。
变色:如果当前结点的父结点和叔父结点是红色,那么:
把父结点和叔父结点变为黑色
把祖父结点变为红色
把指针定义到祖父结点
左旋:当前结点是右子树,且父结点是红色,叔父结点是黑色,对它的父结点左旋。
右旋:当前结点是左子树,且父结点是红色,叔父结点是黑色,那么:
把父结点变为黑色
把祖父结点变为红色
对祖父结点右旋
由于红黑树本来就是平衡二叉搜索树,并且搜索也不会破坏树的平衡,所以搜索算法也与平衡二叉搜索树一致:
具体步骤如下:
从根结点开始检索,把根结点设置为当前结点。
若当前结点为空,返回 null。
若当前结点不为空,比较当前结点 key 与搜索 key 的大小。
若当前结点 key 等于搜索 key,那么该 key 就是搜索目标,返回当前结点。
若当前结点 key 大于搜索 key,把当前结点的左子结点设置为当前结点,重复步骤 2。
若当前结点 key 小于搜索 key,把当前结点的右子结点设置为当前结点,重复步骤 2。
红黑树插入操作分为下面两步:
具体步骤如下:
从根结点开始检索。
若根结点为空,那么插入结点设为根结点,结束。
若根结点不为空,那么把根结点设为当前结点。
若当前结点为 nil,返回当前结点的父结点,结束。
若当前结点 key 等于搜索 key,那么该 key 所在结点就是插入结点,更新结点的值,结束。
若当前结点 key 大于搜索 key,把当前结点的左子结点设置为当前结点,重复步骤 4。
若当前结点 key 小于搜索 key,把当前结点的右子结点设置为当前结点,重复步骤 4。
建议新添加的结点默认为红色,因此这样能够让红黑树的性质尽快满足。不过如果添加的结点是根结点,设为黑色即可。
总结一下红黑树插入可能出现的所有场景:
① 场景 1:红黑树为空树
② 场景 2:插入结点的 key 已存在
处理:
将插入结点设为将要替换结点的颜色
更新当前结点的值为插入结点的值
③ 场景 3:插入结点的父结点为黑色
处理:直接插入。
④ 场景 4:插入结点的父结点为红色
场景 4.1:存在叔父结点,且为红色
由红黑树性质 4 可知:红色结点不能连续。那么此时该插入子树的红黑层数的情况是:黑-红-红。显然最简单的处理方式就是将其改为:红-黑-红。
处理:
将父结点和叔父结点变为黑色
将祖父结点变为红色
将祖父结点设置为当前插入结点
场景 4.2:叔父结点不存在或为黑色,插入结点的父结点是祖父结点的左子结点这种场景下,叔父结点所在的子树的黑色结点就比父结点所在子树的多,不满足红黑树的性质 5。
场景 4.2.1:插入结点是左子树
处理:
将父结点变为黑色
将祖父结点变为红色
将祖父结点右旋
场景 4.2.2:插入结点是左子树
这种场景显然可以转换为 4.2.1。
处理:
将父结点进行左旋
将父结点设为插入结点,得到场景 4.2.1
进行场景 4.2.1 的处理
场景 4.3:叔父结点不存在或为黑色,插入结点的父结点是祖父结点的右子结点相当于场景 4.2 的方向反转,直接看图。
场景 4.3.1:插入结点是左子树
处理:
将父结点变为黑色
将祖父结点变为红色
对祖父结点进行左旋
场景 4.3.2:插入结点是右子树
处理:
将父结点进行右旋
将父结点设置为插入结点,得到场景 4.3.1
进行场景 4.3.1 的处理
下面举个例子,往一棵红黑树中插入元素,整棵树的变换如下图所示:
红黑树删除操作也分为两步:
定位删除位置可以复用红黑树搜索的操作。
如果不存在目标结点,忽略本次操作;如果找到目标结点,删除后进行自平衡处理。
二叉搜索树删除的时候可能出现三种场景:
若删除结点无子结点,直接删除即可。
若删除结点只有一个子结点,用子结点替换删除结点。
若删除结点有两个子结点,用**后继结点(大于删除结点的最小结点)**替换删除结点。
具体应用,可以借助这张图理解:
我们可以发现,另外两种二叉树的删除场景都可以通过相互转换变为场景一。
综上所述,删除的结点可以看作删除替换结点,且替换结点最后总是在树末。
下面总结一下红黑树删除可能出现的所有场景:
为了方面理解,我们先约定一下结点的叫法:
R:替换结点
P:替换结点的父结点
S:替换结点的兄弟结点
SL:兄弟结点的左子结点
SR:兄弟结点的右子结点
灰色:结点颜色可能是红色,也可能是黑色
注意:R 是即将被替换到删除结点的位置的替换结点,在删除前,它还在原来所在位置参与树的子平衡,平衡后再替换到删除结点的位置,才算删除完成。
①场景 1:替换结点为红色
我们把替换结点换到了删除结点的位置时,由于替换结点为红色,删除了不会影响红黑树的平衡,只要把替换结点的颜色变为删除的结点的颜色即可重新平衡。
处理:替换结点颜色变为删除结点的颜色。
②场景 2:替换结点为黑色
当替换结点是黑色时,就必须进行自平衡处理了,我们可以通过区分替换结点是其父结点的左子结点还是右子结点,来做不同的旋转,使树重新平衡。
场景 2.1:替换结点是左子树
场景 2.1.1:替换结点的兄弟结点为红色
若兄弟结点是红结点,那么根据红黑树性质 4,兄弟结点的父结点和子结点肯定为黑色,按照下图方式处理,得到删除场景 2.1.2.3。
处理:
将兄弟结点变为黑色
将父结点变为红色
对父结点进行左旋,得到场景 2.1.2.3
进行场景 2.1.2.3 的处理
场景 2.1.2:替换结点的兄弟结点为黑色
当兄弟结点为黑时,其父结点和子结点的具体颜色也无法确定,此时又得考虑多种子场景。
场景 2.1.2.1:替换结点的兄弟结点的右子结点为红色,左子结点任意颜色
即将删除的左子树的一个黑色结点,显然左子树的黑色结点少 1 了,然而右子结点又是红色,那么我们直接向右子树“借”个红结点来补充黑结点,并进行旋转处理。
处理:
将兄弟结点的颜色变为父结点的颜色
将父结点变为黑色
将兄弟结点的右子结点变为黑色
对父结点进行左旋
场景 2.1.2.2:替换结点的兄弟结点的右子结点为黑色,左子结点为红色
兄弟结点所在的子树有红结点,又可以向兄弟子树“借”个红结点过来,这就转换回了场景 2.1.2.1。
处理:
将兄弟结点变为红色
将兄弟结点的左子结点变为黑色
对兄弟结点进行右旋,得到场景 2.1.2.1
进行场景 2.1.2.1 的处理
场景 2.1.2.3:替换结点的兄弟结点的子结点都为黑色
兄弟子树没有红结点可以“借”了,再向父结点“借”。如果父结点是黑色,为了让父结点在所在的子树中保证平衡(替换结点即将删除,少了一个黑色结点,子树也需要少一个)先把兄弟结点变为红色,再让父结点成为新的替换结点。
处理:
如果父结点为黑色:将兄弟结点变为红色;将父结点作为新的替换结点;重新进行删除结点的场景处理。
如果父结点为红色:替换结点的父结点和替换结点的兄弟结点颜色交换;删除结点和替换结点的值交换后,删除替换结点。
场景 2.2:替换结点是右子树
实际上是场景 2.1 的镜像操作。
场景 2.2.1:替换结点的兄弟结点为红色
处理:
将兄弟结点变为黑色
将父结点变为红色
对父结点进行右旋,得到场景 2.2.2.3
进行场景 2.2.2.3 的处理
场景 2.2.2:替换结点的兄弟结点为黑色
场景 2.2.2.1:替换结点的兄弟结点的左子结点为红色,右子结点任意颜色
处理:
将兄弟结点的颜色变为父结点的颜色
将父结点变为黑色
将兄弟结点的左子结点变为黑色
对父结点进行右旋
场景 2.2.2.2:替换结点的兄弟结点的左子结点为黑色,右子结点为红色
处理:
将兄弟结点变为红色
将兄弟结点的右子结点设为黑色
对兄弟结点进行左旋,得到场景 2.2.2.1
进行场景 2.2.2.1 的处理
场景 2.2.2.3:替换结点的兄弟结点的子结点都为黑色
处理:
如果父结点为黑色:将兄弟结点变为红色;将父结点作为新的替换结点;重新进行删除结点的场景处理。
如果父结点为红色:替换结点的父结点和替换结点的兄弟结点颜色交换;删除结点和替换结点的值交换后,删除替换结点。
原文链接:https://mp.weixin.qq.com/s/UwV_WCTcruaoYCs6R9WHpQ
标签:bbs weixin 增加 tor 默认 style key 因子 并且
原文地址:https://www.cnblogs.com/john1015/p/13204615.html